Andamio Logo
Platform Guides/Developers

API Integration

Complete workflow for building, signing, and tracking Andamio transactions

API Integration Guide

This guide walks through the complete flow for integrating with the Andamio API — from authentication through transaction confirmation.

API Reference: For endpoint schemas, request/response formats, and parameter details, see the Andamio API Reference.

Authentication

All Andamio API requests require two authentication headers:

API Key (Application)

X-API-Key: <your-api-key>

API keys identify your application. Contact the Andamio team to obtain one for your project.

JWT (User)

Authorization: Bearer <jwt>

JWTs authenticate individual users via wallet signature. The flow:

  1. User connects their wallet
  2. Your app requests a challenge from /api/v2/auth/challenge
  3. User signs the challenge with their wallet
  4. Your app exchanges the signature for a JWT via /api/v2/auth/verify

JWTs expire after a configurable period (typically 24 hours).

Transaction Flow

Every Andamio transaction follows a 6-step lifecycle:

BUILD → SIGN → SUBMIT → REGISTER → CONFIRM → UPDATE

Here's the complete flow with code:

1. Build the Transaction

Request an unsigned transaction from the appropriate build endpoint.

const API = "https://preprod.api.andamio.io/api/v2";

const headers = {
  "Content-Type": "application/json",
  "X-API-Key": APP_API_KEY,
  "Authorization": `Bearer ${userJwt}`,
};

// Example: Student committing to an assignment
const buildRes = await fetch(`${API}/tx/course/student/assignment/commit`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    alias: "student1",
    course_id: "abc123...",
    slt_hash: "deadbeef...",
    assignment_info: "sha256:...",
    initiator_data: {
      usedAddresses: [...],
      changeAddress: "addr1...",
    },
  }),
});

const { unsigned_tx } = await buildRes.json();

2. Sign with Wallet

Use the browser wallet API to sign the transaction.

// CIP-30 wallet API
const signedTx = await wallet.signTx(unsigned_tx);

3. Submit to Cardano

Submit the signed transaction to the network.

const txHash = await wallet.submitTx(signedTx);

4. Register with State Machine

Tell Andamio to track this transaction.

await fetch(`${API}/tx/register`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    tx_hash: txHash,
    tx_type: "assignment_submit",
  }),
});

5. Monitor Status

Track the transaction until it reaches a terminal state.

Option A: Server-Sent Events (recommended)

const events = new EventSource(`${API}/tx/stream/${txHash}`);

events.addEventListener("state_change", (e) => {
  const { old_state, new_state } = JSON.parse(e.data);
  console.log(`${old_state} → ${new_state}`);
});

events.addEventListener("complete", (e) => {
  const { final_state } = JSON.parse(e.data);
  console.log(`Transaction complete: ${final_state}`);
  events.close();
});

Option B: Polling

const checkStatus = async () => {
  const res = await fetch(`${API}/tx/status/${txHash}`, { headers });
  const { state } = await res.json();

  if (["updated", "failed", "expired"].includes(state)) {
    return state; // Terminal
  }

  await new Promise(r => setTimeout(r, 5000));
  return checkStatus(); // Keep polling
};

Draft-First vs Direct-to-Chain

For transactions involving commitments (assignments, tasks), you have two integration paths:

Create a database record before the blockchain transaction. Users see immediate feedback.

// 1. Create draft (instant UI feedback)
await fetch(`${API}/course/student/commitment/create`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    course_id: "abc123...",
    course_module_code: "MODULE_01",
    evidence: { submission_url: "https://..." },
  }),
});

// 2. Build, sign, submit TX (as above)
// 3. Register with state machine
// 4. On confirmation, draft status updates to ON_CHAIN

Direct-to-Chain

Skip the draft step. The state machine creates the database record automatically on confirmation.

// 1. Build, sign, submit TX directly
// 2. Register with state machine
// 3. On confirmation, state machine creates DB record

Both paths result in the same final state. Draft-first provides better UX for multi-step submissions.

TX Type Reference

When registering a transaction, use the correct tx_type:

Build EndpointTX TypeNotes
/tx/global/general/access-token/mintaccess_token_mint
/tx/instance/owner/course/createcourse_create
/tx/course/owner/teachers/manageteachers_update
/tx/course/teacher/modules/managemodules_manage
/tx/course/teacher/assignments/assessassessment_assess
/tx/course/student/assignment/commitassignment_submitFirst or update
/tx/course/student/assignment/updateassignment_submitSame as commit
/tx/course/student/credential/claimcredential_claim
/tx/instance/owner/project/createproject_create
/tx/project/owner/managers/managemanagers_manage
/tx/project/owner/contributor-blacklist/manageblacklist_update
/tx/project/manager/tasks/managetasks_manage
/tx/project/manager/tasks/assesstask_assess
/tx/project/contributor/task/commitproject_joinJoin + commit
/tx/project/contributor/credential/claimproject_credential_claimRequires task_hash metadata

Note: Some endpoints share the same TX type. See TX Type Mapping for details.

Complete Example: Assignment Submission

Here's a full working example combining all steps:

import { BrowserWallet } from "@meshsdk/core";

const API = "https://preprod.api.andamio.io/api/v2";

async function submitAssignment(
  wallet: BrowserWallet,
  apiKey: string,
  jwt: string,
  courseId: string,
  moduleCode: string,
  evidenceUrl: string
) {
  const headers = {
    "Content-Type": "application/json",
    "X-API-Key": apiKey,
    "Authorization": `Bearer ${jwt}`,
  };

  // 1. Create draft for immediate feedback
  await fetch(`${API}/course/student/commitment/create`, {
    method: "POST",
    headers,
    body: JSON.stringify({
      course_id: courseId,
      course_module_code: moduleCode,
      evidence: { submission_url: evidenceUrl },
    }),
  });

  // 2. Get wallet info
  const usedAddresses = await wallet.getUsedAddresses();
  const changeAddress = await wallet.getChangeAddress();
  const alias = await getAliasFromAccessToken(wallet); // Your helper

  // 3. Build unsigned TX
  const buildRes = await fetch(`${API}/tx/course/student/assignment/commit`, {
    method: "POST",
    headers,
    body: JSON.stringify({
      alias,
      course_id: courseId,
      slt_hash: await getSltHash(courseId, alias), // Your helper
      assignment_info: hashEvidence(evidenceUrl),  // SHA256
      initiator_data: { usedAddresses, changeAddress },
    }),
  });
  const { unsigned_tx } = await buildRes.json();

  // 4. Sign
  const signedTx = await wallet.signTx(unsigned_tx);

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

  // 6. Register
  await fetch(`${API}/tx/register`, {
    method: "POST",
    headers,
    body: JSON.stringify({
      tx_hash: txHash,
      tx_type: "assignment_submit",
    }),
  });

  // 7. Wait for confirmation
  return new Promise((resolve, reject) => {
    const events = new EventSource(`${API}/tx/stream/${txHash}`);

    events.addEventListener("complete", (e) => {
      const { final_state } = JSON.parse(e.data);
      events.close();

      if (final_state === "updated") {
        resolve({ success: true, txHash });
      } else {
        reject(new Error(`Transaction ${final_state}`));
      }
    });

    // Timeout after 10 minutes
    setTimeout(() => {
      events.close();
      reject(new Error("Timeout waiting for confirmation"));
    }, 600000);
  });
}

Next Steps