Skip to content

Credentials API

Issue, list, and retrieve credentials.

All endpoints require an API key and operate within the organization the key belongs to.

Issue a credential

POST /api/v1/credentials

Request body

{
  "badgeId": "550e8400-e29b-41d4-a716-446655440000",
  "recipient": {
    "name": "Jane Doe",
    "email": "jane@example.com"
  }
}
Field Type Required Notes
badgeId UUID ✅ The badge template UUID (from the Badges API).
recipient.name string ✅ Display name on the credential.
recipient.email string ✅ Recipient's email. The credential is sent here and matched at claim time.

Response

{
  "id": "f1d2c3b4-...",
  "credentialId": "CRD-ZBUYQMTA",
  "verificationUrl": "https://credentials.certdirectory.io/verify/CRD-ZBUYQMTA",
  "status": "pending",
  "issuanceDate": "2026-01-15T10:00:00.000Z",
  "expirationDate": "2028-01-15T10:00:00.000Z",
  "recipientName": "Jane Doe",
  "recipientEmail": "jane@example.com",
  "supersedesCredentialId": null
}

Side effects

  • An email is sent to the recipient with the verification link, unless your organization has email notifications disabled.
  • If the recipient already has a credential of this badge:
    • Expired → the new credential supersedes the expired one (supersedesCredentialId is set).
    • Active, time-bound → the existing credential is revoked, and the new one supersedes it.
    • Revoked → the new credential supersedes the revoked one.
    • Active, non-expiring → returns 409 CONFLICT. Revoke the existing credential first.

Errors

Status Code Reason
400 VALIDATION_ERROR Missing or malformed fields.
403 FORBIDDEN Plan doesn't allow API access, or org isn't approved, or signing keys not generated.
404 NOT_FOUND badgeId not found or not in your organization.
409 CONFLICT Recipient already has an active, non-expiring credential.
429 RATE_LIMITED Per-key or quota limit hit.

List credentials

GET /api/v1/credentials

Query parameters

Param Type Default Notes
limit integer 50 Page size. Max 100.
offset integer 0 Pagination.
badgeId UUID – Filter by badge.
status string – Filter by valid, pending, expired, or revoked.

Response

{
  "data": [
    {
      "id": "...",
      "credentialId": "CRD-ZBUYQMTA",
      "badgeId": "...",
      "badgeName": "AWS Fundamentals",
      "recipientName": "Jane Doe",
      "recipientEmail": "jane@example.com",
      "status": "valid",
      "issuanceDate": "2026-01-15T10:00:00.000Z",
      "expirationDate": "2028-01-15T10:00:00.000Z",
      "claimedAt": "2026-01-15T11:30:00.000Z",
      "revokedAt": null,
      "isPublic": true,
      "verificationUrl": "https://credentials.certdirectory.io/verify/CRD-ZBUYQMTA",
      "supersedesCredentialId": null,
      "supersededByCredentialId": null
    }
  ],
  "pagination": {
    "limit": 50,
    "offset": 0,
    "total": 247
  }
}

Get a credential

GET /api/v1/credentials/{credentialId}

credentialId is the credential's internal UUID (the value of id, not the CRD-... short ID).

Response

Same shape as a single item from the list endpoint, plus the full OB3 JSON-LD payload under ob3Credential.

Revoke a credential

Revocation via the v1 API is not yet available. To revoke programmatically today, use the session-authenticated endpoint:

DELETE /api/organizations/{orgId}/credentials/{credentialId}
Authorization: Bearer <session JWT>
Content-Type: application/json

{ "reason": "Recipient withdrew from program." }

A v1 endpoint for revocation is on the roadmap. See Revoking Credentials for the in-app flow.

Verify a credential (public)

No authentication required:

GET /api/verify/{credential-id}

credential-id here is either the short ID (CRD-…) or the UUID. See Verifying a Credential for the response shape.

Get the OB3 JSON-LD (public)

GET /credentials/{credential-id}/ob3

Returns the signed Open Badges 3.0 JSON-LD payload. Returns 403 if the credential is private and the request is unauthenticated.

Get the baked PNG (public)

GET /api/credentials/{credential-id}/baked

Returns the badge image with the OB3 credential embedded as PNG metadata. Useful for portable, file-based credentials.

Quota

Each unique recipient email per organization per calendar month counts once toward your monthly quota:

Plan Recipients/month
Free 1,000
Professional 1,000
Enterprise 5,000

Exceeding the quota returns 403 FORBIDDEN with a message indicating the limit. The counter resets on the 1st of each month.

Next