Developer Docs
Sandbox · API v2PlaygroundGet API keys

Errors & conventions

Assert these in every integration. The platform's promise is that refusals are self-diagnosing, the error body alone should tell a misconfigured client what to fix.

Universal request conventions

ConventionContract
AuthenticationAuthorization: Bearer <api-key-or-oauth-token> on every call. x-api-key-shaped headers also pass the edge.
IdempotencyIdempotency-Key: <uuid> on mutating POSTs. Required on publish-dpp and oauth/token; accepted & recommended elsewhere.
Content typeapplication/json unless the endpoint says multipart.
ErrorsTyped envelope { error: { code, message, details? } }.
CorrelationOptional X-Correlation-Id flows route → use case → audit log.

The error envelope

JSON
{
  "error": {
    "code": "API_SCOPE_DENIED",
    "message": "Missing required scope for this operation.",
    "details": { "required": "products:create", "available": ["products:view"] }
  }
}

Error catalogue

The codes an integrator must branch on. Two are documented deviations, asserted as-is until the underlying contract changes.

StatusCodeTrigger
401-Bad/garbage or missing credential; also role-level permission denials.
403API_SCOPE_DENIEDGood credential, missing scope. Carries { required, available }.
403FORBIDDENOut-of-scope on publish-dpp, different auth wrapper. Deviation.
403SANDBOX_EXPIREDSandbox tenant past expiry (both conditions required).
403GUARD_REJECTEDIllegal passport lifecycle transition (e.g. EDIT a published passport).
400VALIDATION_ERRORField validation, names the field/bound (e.g. 21-char serial, description < 10).
422EXTENSIONS_REQUIREDMissing extensions on product create.
422SCHEMA_NOT_FOUNDCategory has no effective schema (see the category-hint gotcha).
400INVALID_GTIN / INVALID_SERIALBad check digit / over-long serial at the resolver.
404PRODUCT_NOT_FOUNDUnknown-but-valid GTIN at the resolver, names gtin/serial/host.
404-Draft/nonexistent public passport; cross-tenant fetch. Authz runs before existence, no id enumeration.
410SERIAL_VOIDEDVoided serialized item, returns a tombstone, zero passport content.
422UNKNOWN_EVENT_TYPEWebhook subscription to a non-catalogue event name.
422PASSPORT_NOT_PUBLISHABLERegistry broadcast attempted on a passport whose lifecycle state is not publishable.
409DUPLICATE_GTINA second model-granularity product with the same GTIN in this tenant, (company_id, gtin) is unique.
409ALREADY_REVOKEDRevoking a key that is already revoked. Note that rotate revokes the old id for you and returns a NEW data.id, revoke that one.
409-Conflict: re-void, same-key different-body idempotency, scheme-lock change, illegal passport command transition.
503COMPLIANCE_EVALUATION_UNAVAILABLEBattery only, and deliberate: the governed battery obligation pack is quarantined, so no battery compliance verdict may be issued. This is NOT_EVALUATED, a fail-closed refusal to judge, never a compliance failure. Every other category evaluates normally; start on electronics.
503REGISTRY_UNAVAILABLENo external registry (CATENA-X / GS1 / EBSI) is configured, the normal sandbox answer to POST /passports/{id}/publish.
503ANCHORING_UNAVAILABLEBlockchain not configured in this environment, the normal sandbox answer.
500ANCHORING_FAILEDA real anchoring fault on a configured chain. Distinct from ANCHORING_UNAVAILABLE (503): only this one means something is wrong.
405-Wrong verb on a route that does not support it. (Product update accepts PATCH, canonical, and PUT as an alias.)
302-Resolver redirect to /p/{uid} (the public uid = the product id).
202-Async accepted (generation, imports).
Rate limiting is enforced: per-tier budgets on a Redis-backed limiter. Over budget → 429 RATE_LIMITED with X-RateLimit-Limit / X-RateLimit-Remaining / X-RateLimit-Reset and Retry-After. Back off on Retry-After.

Idempotency semantics

  • Same key + same body → the first result is replayed, with no duplicate side effects.
  • Same key + different body → a typed conflict.
  • Enforcement is per-route: publish-dpp and oauth/token require a key; POST /products without one still returns 201.
Was this page helpful?
Thanks, noted.Feedback goes to the docs team by email.