API Reference

All requests are JSON over HTTPS. Authenticate with X-API-KEY: zada_….

POST/api/v1/issueX-API-KEY (scope: issue)

Sign a payload through the full ZADA pipeline and (optionally) render an A4 PDF. `subject` is optional and becomes the JWT `sub` claim — use it for personal credentials, omit it for bearer credentials.

Request
{
  "issuer_id": "did:key:z6Mk…",   // required — the HOT SD-JWT issuer DID (not the cold DID)
  "subject": "patient-1234",      // optional — JWT 'sub' claim, identifies who the credential is about
  "claims": { "type": "HealthCertificate", "result": "negative" },
  "generate_pdf": true             // optional, default true
}
Response
{
  "zada": "ZADA:6BFOXN%TS3DH...",
  "pdf_url": "https://.../credentials/<tenant>/<id>.pdf",
  "issuer": { "id": "...", "did": "did:key:zCold…", "linked_hot_did": "did:key:zHot…", "name": "...", "kid": "...", "x5t_s256": "..." },
  "issued_at": "2026-04-20T12:00:00Z"
}
POST/api/v1/verifyX-API-KEY (scope: verify)

Decode and verify a ZADA string against the cached trust registry.

Request
{ "zada": "ZADA:6BFOXN%..." }
Response
{
  "valid": true,
  "issuer": { "name": "...", "kid": "...", "x5t_s256": "...", "linked_hot_did": "did:key:zHot…", "trust_source": "registry_cache" },
  "algorithm": "EdDSA",
  "claims": { ... }
}
GET/api/v1/registry/syncPublic

Returns a CBOR-encoded, COSE_Sign1-signed bundle of trusted issuers for offline verification.

Request
Response
Binary application/cose payload (download as zada-trust-bundle.cose)
POST/api/v1/registry/refreshX-API-KEY (scope: issue)

Pull latest entries from the configured ZADA mirror (env MOCK_ZADA_URL) into trust_registry_cache. Seeds 2 mock issuers if no URL is set.

Request
Response
{ "refreshed": 2, "source": "mock-seed" }

Pipeline format

Claims
  └─ CBOR encode
      └─ COSE_Sign1 (alg: EdDSA, kid, x5t#S256)
          └─ ZLIB deflate
              └─ Base45 encode
                  └─ Prepend "ZADA:"
                      └─ QR code (Level H)

Before you start

  1. Create an API key in Dashboard → API Keys (the secret is shown once).
  2. Create a cold issuer in Dashboard → Issuers and link it to your existing hot SD-JWT issuer's did:key:….

Cold credentials are signed by a separate cold-system key, but you address issuers by their hot SD-JWT DID. The cold system handles the mapping — wallets only need to know one DID per issuer.

Issue a credential

curl -X POST https://coldcred.zada.solutions/api/v1/issue \
  -H "X-API-KEY: zada_xxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "issuer_id": "did:key:zDnaepLCSitGvvprHJZFjaC61aFUmHrKcGLGEkBrf1PAceUna",
    "subject": "patient-1234",
    "claims": { "type": "HealthCertificate", "result": "negative" },
    "generate_pdf": true
  }'

The issuer_id is the hot SD-JWT issuer DID — exactly the one your wallet already has. The iss claim inside the signed cold credential is the cold signing DID (so verifiers can resolve the public key from the credential alone). Use linked_hot_did in the verify response to display the familiar hot identity to users.

The subject field (optional)

subject is a free-form string identifying who the credential is about. It maps to the standard JWT sub claim inside the signed payload, is stored on the credential row (so you can list/filter by it later), is rendered on the PDF, and is returned inside the verified claims on /verify. The cold system never interprets it — pick whatever identifier makes sense for your use case (patient ID, employee email, student number, VIN, etc.).

Omit it for bearer-style credentials (event tickets, vouchers, anything not tied to a specific person). When omitted, sub in the signed payload is null.

With subject (personal)
curl -X POST https://coldcred.zada.solutions/api/v1/issue \
  -H "X-API-KEY: zada_…" \
  -H "Content-Type: application/json" \
  -d '{
    "issuer_id": "did:key:zDnaepLCSitGvvprHJZFjaC61aFUmHrKcGLGEkBrf1PAceUna",
    "subject": "patient-1234",
    "claims": {
      "type": "HealthCertificate",
      "result": "negative",
      "testedAt": "2026-04-24"
    }
  }'
Without subject (bearer)
curl -X POST https://coldcred.zada.solutions/api/v1/issue \
  -H "X-API-KEY: zada_…" \
  -H "Content-Type: application/json" \
  -d '{
    "issuer_id": "did:key:zDnaepLCSitGvvprHJZFjaC61aFUmHrKcGLGEkBrf1PAceUna",
    "claims": {
      "type": "EventTicket",
      "event": "ZADA Summit 2026",
      "seat": "A12"
    }
  }'

Verify a credential

curl -X POST https://coldcred.zada.solutions/api/v1/verify \
  -H "X-API-KEY: zada_xxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{ "zada": "ZADA:6BFOXN%TS3DH..." }'

Download offline trust bundle

Public — no authentication required.

curl https://coldcred.zada.solutions/api/v1/registry/sync \
  -o zada-trust-bundle.cose

Refresh trust registry

curl -X POST https://coldcred.zada.solutions/api/v1/registry/refresh \
  -H "X-API-KEY: zada_xxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxx"