Skip to main content
Read concepts first: Issue on Behalf For JWT/JWKS setup and signing examples, see Partner Authentication.

Base URLs

EnvironmentBase URL
Sandboxhttps://api.sandbox.mocachain.org/v1
Productionhttps://api.mocachain.org/v1

1) Issue credential

Submit credential issuance for a target user.
POST /v1/credentials/issue-on-behalf

Request headers

HeaderRequiredValue
Content-TypeYesapplication/json
x-partner-authYesSigned Partner JWT
user-agentRecommendedIdentifiable service string

Request body

{
  "issuerDid": "did:air:id:test:5P44fsVUhPctDTWH2Nz26pZJFsg6CqyiAELTGeVQDB",
  "credentialId": "c21s70g0i54sn0023172Cv",
  "credentialSubject": {
    "age": 25,
    "location": "United States",
    "isMember": true
  },
  "onDuplicate": "revoke"
}
FieldTypeRequiredDescription
issuerDidstringYesYour Issuer DID from Developer Dashboard
credentialIdstringYesIssuance Program ID
credentialSubjectobjectYesCredential data matching your schema
onDuplicatestringNoDuplicate behavior: "ignore" returns existing credential, "revoke" reissues (default: "revoke")

Response

{
  "coreClaimHash": "239704895d0c4693d33ee7ab9e37c6eb295f178d80074a32d4ace53b1a779c44",
  "credentialId": "c21s70g0i54sn0023172Cv",
  "userUuid": "7f01c42c-02cf-4325-96ed-ba034700f724"
}
FieldDescription
coreClaimHashUnique issuance identifier used for status polling
credentialIdProgram ID (echoed)
userUuidTarget user UUID
Issuance is asynchronous. Use coreClaimHash with the status endpoint to confirm ONCHAIN.

2) Check issuance status

Poll issuance status by coreClaimHash.
GET /v1/credentials/status?coreClaimHash={hash}

Request headers

HeaderRequiredValue
x-partner-authYesSigned Partner JWT

Query parameters

ParameterRequiredDescription
coreClaimHashYesValue returned by issue endpoint

Response

{
  "coreClaimHash": "239704895d0c4693d33ee7ab9e37c6eb295f178d80074a32d4ace53b1a779c44",
  "issuanceDate": "2026-01-19 07:18:45",
  "issuerDid": "did:air:id:test:5P44fsVUhPctDTWH2Nz26pZJFsg6CqyiAELTGeVQDB",
  "vcId": "c28t30c048pe502a3713w0",
  "vcStatus": "ONCHAIN"
}
vcStatus values:
StatusMeaning
WAIT_ONCHAINIssuance in progress
ONCHAINCredential is available on-chain

Examples

Basic issue + poll (Node.js)

require("dotenv").config();
const { getPartnerJwt } = require("./lib/jwt");

const BASE_URL =
  process.env.NODE_ENV === "production"
    ? "https://api.mocachain.org/v1"
    : "https://api.sandbox.mocachain.org/v1";

async function issueOnBehalf(userEmail, credentialSubject) {
  const token = await getPartnerJwt(userEmail);

  const res = await fetch(`${BASE_URL}/credentials/issue-on-behalf`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-partner-auth": token
    },
    body: JSON.stringify({
      issuerDid: process.env.ISSUER_DID,
      credentialId: process.env.CREDENTIAL_ID,
      credentialSubject,
      onDuplicate: "revoke"
    })
  });

  if (!res.ok) throw new Error(`Issue failed: ${res.status}`);
  return res.json();
}

async function getStatus(userEmail, coreClaimHash) {
  const token = await getPartnerJwt(userEmail);
  const res = await fetch(
    `${BASE_URL}/credentials/status?coreClaimHash=${encodeURIComponent(coreClaimHash)}`,
    { headers: { "x-partner-auth": token } }
  );
  if (!res.ok) throw new Error(`Status failed: ${res.status}`);
  return res.json();
}

Event-driven webhook example

app.post("/webhooks/kyc-complete", async (req, res) => {
  const { userEmail, kycLevel } = req.body;

  const issued = await issueOnBehalf(userEmail, {
    kycVerified: true,
    kycLevel,
    verifiedAt: new Date().toISOString()
  });

  res.json({ success: true, coreClaimHash: issued.coreClaimHash });
});

Retry with backoff

async function issueOnBehalfWithRetry(userEmail, credentialSubject, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await issueOnBehalf(userEmail, credentialSubject);
    } catch (error) {
      if (attempt === maxRetries) throw error;
      await new Promise((r) => setTimeout(r, Math.pow(2, attempt) * 1000));
    }
  }
}

Error reference

HTTP statusLikely causeSuggested fix
400Missing/invalid request fieldsVerify issuerDid, credentialId, credentialSubject
401Invalid/expired JWT, JWKS mismatchValidate signature, kid, typ: "JWT", token expiry
403Feature not enabled or schema not allowedEnable feature in dashboard, verify schema ownership
404Unknown issuer/programRecheck IDs in dashboard
409Consent rejected / conflictCheck user consent and duplicate behavior
500Server-side failureRetry with backoff and inspect logs

Troubleshooting

  • If issue call succeeds but credential is not visible, poll status until ONCHAIN.
  • If you get authentication errors, verify JWT contains partnerId, scope: "issue", email and header includes typ: "JWT".
  • Ensure JWKS endpoint is public and kid maps to the signing key.