Andamio Logo
Protocol/Protocol V2/Transaction State Machine/Course

Manage Modules

API integration guide for creating, updating, and removing course modules

Manage Modules

Teachers can create, update, and remove learning modules within a course in a single batch transaction. Each module represents a Student Learning Target (SLT) and holds the criteria students must satisfy to earn credit.

Summary

PropertyValue
SystemCourse
RoleTeacher
Type Keymodules_manage
Build EndpointPOST /api/v2/tx/course/teacher/modules/manage
DB SyncYes
Service Fee0 ADA
Est. Wallet Cost~1.86 ADA per module

The wallet cost scales with the number of modules: transaction fee ~0.27 ADA + UTxO deposit ~1.59 ADA per module minted. Approximate costs: 5 modules ~8.4 ADA, 10 modules ~16.5 ADA. UTxO deposits are recoverable when modules are burned.

Build Transaction

Endpoint

POST /api/v2/tx/course/teacher/modules/manage

Request Body

{
  "alias": "teacher1",
  "courseId": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
  "modulesToMint": [
    {
      "slts": ["Learner can deploy a smart contract to preprod"],
      "allowedStudents_V2": [],
      "prerequisiteAssignments_V2": []
    },
    {
      "slts": ["Learner can write unit tests for a Plutus validator"],
      "allowedStudents_V2": [],
      "prerequisiteAssignments_V2": ["9f3a1b2c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a"]
    }
  ],
  "modulesToUpdate": [],
  "modulesToBurn": [],
  "walletData": {
    "usedAddresses": ["addr1qx2fxv..."],
    "changeAddress": "addr1qx2fxv..."
  }
}
FieldTypeRequiredDescription
aliasstring (Alias)YesThe teacher's access token alias. Must be in the course's teacher list.
courseIdstring (GYMintingPolicyId)YesThe 56-character hex course policy ID.
modulesToMintMintModuleV2[]YesArray of new modules to create. Pass an empty array if not creating any.
modulesToMint[].sltsstring[]YesStudent Learning Targets — descriptions of what the student must demonstrate.
modulesToMint[].allowedStudents_V2string[] (GYMintingPolicyId[])YesPolicy IDs of courses whose students may access this module. Empty array means open to all enrolled students.
modulesToMint[].prerequisiteAssignments_V2string[] (SltHash[])Yes64-character hex hashes of modules that must be completed before this one. Empty array means no prerequisites.
modulesToUpdateUpdateModuleV2[]YesArray of existing modules to update. Pass an empty array if not updating any.
modulesToUpdate[].sltHashstring (SltHash)Yes64-character hex hash identifying the module to update.
modulesToUpdate[].allowedStudents_V2string[] (GYMintingPolicyId[])YesUpdated list of allowed student course policy IDs.
modulesToUpdate[].prerequisiteAssignments_V2string[] (SltHash[])YesUpdated list of prerequisite module hashes.
modulesToBurnstring[] (SltHash[])Yes64-character hex hashes of modules to remove. Pass an empty array if not removing any.
walletDataWalletDataNoObject containing usedAddresses (string[]) and changeAddress (string).

Response

{
  "unsignedTxCBOR": "84a800..."
}

The response contains the unsigned transaction CBOR, ready for the user's wallet to sign.

Register Transaction

After the user signs and submits the transaction, register it with the state machine:

POST /api/v2/tx/register

{
  "tx_hash": "64-char hex hash from wallet.submitTx()",
  "tx_type": "modules_manage"
}

No metadata is required. On confirmation, the state machine batch-confirms all created modules and removes all burned modules from the database.

EndpointDescription
GET /api/v2/courses/{course_id}/modulesList all modules for the course, including SLT hashes and prerequisite chains

Example: Full Lifecycle

const API_URL = "https://api.andamio.io";

// 1. Build — create two modules, one with a prerequisite
const buildRes = await fetch(`${API_URL}/api/v2/tx/course/teacher/modules/manage`, {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-API-Key": API_KEY,
    "Authorization": `Bearer ${userJwt}`,
  },
  body: JSON.stringify({
    alias: "teacher1",
    courseId: "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
    modulesToMint: [
      {
        slts: ["Learner can deploy a smart contract to preprod"],
        allowedStudents_V2: [],
        prerequisiteAssignments_V2: [],
      },
    ],
    modulesToUpdate: [],
    modulesToBurn: [],
    walletData: {
      usedAddresses: [walletAddress],
      changeAddress: walletAddress,
    },
  }),
});
const { unsignedTxCBOR } = await buildRes.json();

// 2. Sign
const signedTx = await wallet.signTx(unsignedTxCBOR);

// 3. Submit
const txHash = await wallet.submitTx(signedTx);

// 4. Register
await fetch(`${API_URL}/api/v2/tx/register`, {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-API-Key": API_KEY,
    "Authorization": `Bearer ${userJwt}`,
  },
  body: JSON.stringify({
    tx_hash: txHash,
    tx_type: "modules_manage",
  }),
});

// 5. Monitor (SSE)
const events = new EventSource(
  `${API_URL}/api/v2/tx/stream/${txHash}`
);
events.addEventListener("state_change", (e) => {
  const data = JSON.parse(e.data);
  console.log(`State: ${data.old_state} → ${data.new_state}`);
});
events.addEventListener("complete", (e) => {
  const data = JSON.parse(e.data);
  console.log(`Final: ${data.final_state}`);
  events.close();
});

See Also