Skip to main content

REST API

The v1 verification API answers read-only questions about Certichain credentials over HTTPS. It needs no wallet. Every success answer is signed, so a caller who pins the deployment's signer address can trust the answer even through caches.

Availability as of 2026-10-24

The API is built but off on the Sepolia deployment until the owner sets its signer key. While off, every endpoint answers 503 CONFIG_MISSING. The signer address to pin is published by the deployment owner as NEXT_PUBLIC_API_SIGNER.

Before you call​

  1. Get the signer address from the deployment owner over a channel you trust, and hard-code it.
  2. Optionally get the chain id and the registry addresses of the contract sets you accept, and pin those too.
  3. Send a fresh random 32-byte nonce with every request (?nonce=0x...) and check that the answer echoes it.
  4. Check every answer as described in API envelope, or use the SDK, which does it for you.

Authentication and limits​

No key is needed. An API key gives a higher budget:

Authorization: Bearer <key>
TierGET per hourPOST /verify per hourCounted per
Anonymous60060IP address (IPv6 per /64)
Standard key6,000600Key

A malformed or unknown key is refused with 401, never served as anonymous. Counted answers carry RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset; a 429 carries Retry-After. Keys are issued by the deployment owner (npm run api:key); the server stores only their SHA-256 hashes.

GET /credentials/{id}​

Chain facts about one credential, looked up in every contract set the deployment lists (active set first).

ParameterWhereRule
idpath0x plus 64 hex characters
noncequery, optional0x plus 64 hex characters
curl "https://certichain.example/api/v1/credentials/0x3333333333333333333333333333333333333333333333333333333333333333?nonce=0x5555555555555555555555555555555555555555555555555555555555555555"

data when found:

FieldTypeMeaning
idhexThe credential id
foundtrue
issueraddressThe key that signed the credential
currentIssueraddressThe key now in charge of that issuer, after any key retirements
issuerRegisteredbooleancurrentIssuer is a registered issuer
issuerSuspendedbooleancurrentIssuer is suspended (all its credentials then count as suspended)
issuerRetiredbooleanThe signing key was retired in favour of a successor
recordTypenumberRecord type id
issuedAtnumberUnix seconds
validUntilnumberUnix seconds, 0 for no expiry
statusstringactive, suspended, revoked or superseded
expiredbooleanvalidUntil is set and passed when the answer was made

When not found, data is { "id": "0x...", "found": false }. "Not found" is only answered when every listed set was read; if one could not be read the answer is 503.

A credential is in good standing when it is found, active, not expired, and its issuer is registered and not suspended. Whether you accept that issuer as an institution is your decision.

POST /verify​

Verifies a verifiable credential file or an Open Badge file exported from Certichain, both offline (signatures, claims) and against the chain.

curl -X POST "https://certichain.example/api/v1/verify?nonce=0x5555555555555555555555555555555555555555555555555555555555555555" \
-H "Content-Type: application/json" \
--data-binary @credential.vc.json
RuleFailure
Content-Type starts with application/json415 (not counted against the budget)
Body at most 65,536 bytes413
The file parses as a Certichain VC or Open Badge400
The file's registry (domain.verifyingContract) is listed by this deployment422 REGISTRY_NOT_LISTED

data fields:

FieldMeaning
kindvc or open-badge
idThe credential id in the file
offlinescope, consistency (do the claims match the signed package), mismatches, id, details, signature (valid or needs-contract-check), expired
onlinechain, credential, anchor, issuerRegistered, active, signature, holder (not-claimed, match, mismatch or unbound)
listsStatus list results, when the file has status list entries
standardProofResult of a badge's standard eddsa-rdfc-2022 proof, when present
fullyVerifiedtrue only when every check passed
limitsPlain sentences about what this verdict does not prove
fileSha256SHA-256 of the exact body received; compare it with your own hash of the file

The answer is always Cache-Control: no-store.

GET /status​

Who is answering: { apiVersion, chainId, registry, signer, sets: [{ registry, active }] }. Use it once to confirm the signer and list the contract sets, then pin them.

GET /openapi.json​

The OpenAPI 3.1 document, unsigned, Cache-Control: public, max-age=86400. It declares servers /api/v1, optional bearer auth, and the Meta, Signature, CredentialData, StatusData, VerifyData and ErrorResponse schemas. The SDK's types are tested against it.

Caching and CORS​

RequestCache-Control
POST /verify, or any request with a nonceno-store
With an API keyprivate, no-store
Anonymous GET without a noncepublic, max-age=15

All endpoints send Access-Control-Allow-Origin: *, so a browser page on another site can call the API. OPTIONS preflights are answered with 204.

Errors​

Errors are plain JSON and not signed: { "error": { "code": "...", "message": "..." } }. Treat an unsigned answer as no answer.

StatusCodeMeaning
400BAD_REQUESTBad id, nonce, body or file
401UNAUTHORIZEDMalformed or unknown API key
413PAYLOAD_TOO_LARGEBody over 65,536 bytes
415UNSUPPORTED_MEDIA_TYPEPOST /verify not sent as JSON
422REGISTRY_NOT_LISTEDThe file names a registry this deployment does not answer for
429RATE_LIMITEDBudget used up; see Retry-After
503CONFIG_MISSINGThe API is off on this deployment
503UPSTREAM_ERRORThe chain or the counter could not be read; retry after Retry-After

Known limits​

  • One POST /verify makes several chain reads (about five) with a 10 second timeout each.
  • For a credential issued by a contract wallet, the server calls the wallet address named in the file to check the signature.
  • Answers can be up to dataMaxAgeSeconds behind the chain (15 seconds by default). For a decision that must see a revocation immediately, read the chain directly.

Source files​

  • src/app/api/v1/*/route.ts
  • src/lib/server/api/handler.ts, credential-lookup.ts, verify-file.ts
  • src/lib/api/openapi.ts, src/lib/api/envelope.ts
  • docs/api/README.md

Tests​

  • src/lib/server/api/handler.test.ts, src/lib/api/openapi.test.ts
  • interop/test/api-answers.test.mjs
  • E2E: e2e/journeys/verification-api.journey.ts