Base URLs
Environment Base URL Sandbox https://api.sandbox.mocachain.org/v1Production https://mocachain-mainnet.api.air3.com/v1
1) Issue credential
Submit credential issuance for a target user.
POST /v1/credentials/issue-on-behalf
Header Required Value Content-TypeYes application/jsonx-partner-authYes Signed Partner JWT user-agentRecommended Identifiable service string
Request body
{
"issuerDid" : "did:air:id:test:5P44fsVUhPctDTWH2Nz26pZJFsg6CqyiAELTGeVQDB" ,
"credentialId" : "c21s70g0i54sn0023172Cv" ,
"credentialSubject" : {
"age" : 25 ,
"location" : "United States" ,
"isMember" : true
},
"onDuplicate" : "revoke"
}
Field Type Required Description issuerDidstring Yes Your Issuer DID from Developer Dashboard credentialIdstring Yes Issuance Program ID credentialSubjectobject Yes Credential data matching your schema onDuplicatestring No Duplicate behavior: "ignore" returns existing credential, "revoke" reissues (default: "revoke")
Response
{
"coreClaimHash" : "239704895d0c4693d33ee7ab9e37c6eb295f178d80074a32d4ace53b1a779c44" ,
"credentialId" : "c21s70g0i54sn0023172Cv" ,
"userUuid" : "7f01c42c-02cf-4325-96ed-ba034700f724"
}
Field Description 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}
Header Required Value x-partner-authYes Signed Partner JWT
Query parameters
Parameter Required Description coreClaimHashYes Value 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:
Status Meaning WAIT_ONCHAINIssuance in progress ONCHAINCredential is available on-chain
Examples
Issue on Behalf — Node.js Reference A minimal Node.js implementation of the issue-then-poll flow: sign the Partner JWT, call POST /credentials/issue-on-behalf, and poll status until ONCHAIN. Clone it to run the end-to-end issuance flow against the sandbox.
Basic issue + poll (Node.js)
require ( "dotenv" ). config ();
const { getPartnerJwt } = require ( "./lib/jwt" );
const BASE_URL =
process . env . NODE_ENV === "production"
? "https://mocachain-mainnet.api.air3.com/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 status Likely cause Suggested fix 400 Missing/invalid request fields Verify issuerDid, credentialId, credentialSubject 401 Invalid/expired JWT, JWKS mismatch Validate signature, kid, typ: "JWT", token expiry 403 Feature not enabled or schema not allowed Enable feature in dashboard, verify schema ownership 404 Unknown issuer/program Recheck IDs in dashboard 409 Consent rejected / conflict Check user consent and duplicate behavior 500 Server-side failure Retry 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.