Guides
Issue a credential
Issue a digital credential from your system of record, retry safely, and confirm delivery with the list endpoint and webhooks.
Updated
On this page
This guide issues one digital credential through the API when a learner completes a course in your own system.
Before you begin
- An API key with the
credentials:writescope. Addcredentials:readif you also want to confirm issuance by listing credentials. - At least one allowed email domain in your organisation's settings. Recipients'
work_emailmust use one of them. - Optionally, a certificate template saved in Templates. Without one, your default template is used.
Every successful request issues a real credential and emails the recipient. There is no test mode. Setting is_historical to true sends no claim email, but it still creates a real archival credential that counts towards your plan.
1. Build the request
Choose a stable Idempotency-Key from your own data, such as your course and learner IDs, and always send issue_date. Together they make retries safe.
async function issueCredential({ learner, course, completedOn }) {
const response = await fetch("https://app.arrota.com/api/v1/credentials", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ARROTA_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": `course-${course.id}-learner-${learner.id}`,
},
body: JSON.stringify({
recipient_name: learner.fullName,
work_email: learner.email,
document_type: "Training Record",
issue_date: completedOn, // YYYY-MM-DD
credential_id: `${course.code}-${learner.id}`,
}),
});
return { status: response.status, body: await response.json() };
}curl -X POST "https://app.arrota.com/api/v1/credentials" \
-H "Authorization: Bearer $ARROTA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: course-4471-learner-208" \
-d '{
"recipient_name": "Alex Rivera",
"work_email": "alex.rivera@example.com",
"document_type": "Training Record",
"issue_date": "2026-09-18",
"credential_id": "SAFETY-101-208"
}'2. Handle the response
202: issued. Storedocument_idandrecord_idagainst the learner.200with"status": "already_issued": this exact issuance happened before, for example on an earlier retry. Store the returneddocument_id; nothing new was created.400: fix the request.fieldslists invalid inputs, andcodenames business rules such asDOMAIN_NOT_ALLOWED.403withSEAT_CAP: your plan's recipient limit is reached.429,500,503: retry the same request later. Because the body and key are unchanged, a retry cannot create a duplicate.
3. Confirm and follow the credential
- List recent credentials with
GET /api/v1/credentials?limit=1to see it. - Subscribe to webhooks:
credential.issuedconfirms issuance,credential.claimedtells you the recipient added it to their Vault, andcredential.revokedtells you if it is withdrawn.
Employment documents and background checks cannot be issued this way. See Credentials for the full field reference.