Scopes
The API is GA on production (app.traceable.digital) as of v0.99.6.1. The API returns 503 FEATURE_DISABLED on any environment where it is not enabled. See Availability.
Each API key carries one or more scopes, fixed at key mint time. A request that needs a scope the key does not hold returns 403 FORBIDDEN_SCOPE. Scopes follow least privilege: read is separate from write, gate-read is separate from product-read, withdraw is separate from submit, and supplier-read is separate from product-read.
Grantable scopes
These are the scopes you can grant when minting a key:
| Scope | Grants |
|---|---|
dpp:read | Read products, traceability, components, GS1 prefixes, and document metadata and download URLs |
dpp:draft:write | Create and update drafts, ingest form data, and write traceability, components, and GS1 prefixes |
dpp:gate:read | Read the publish gate and the readiness report |
dpp:submit | Submit a product for review |
dpp:withdraw | Withdraw a published product (distinct from submit) |
document:write | Upload evidence documents |
category:read | List authorized industries and categories |
schema:read | Read a category's field schema, schema diff, and product schema-compatibility |
supplier:read | List owned-or-linked suppliers (distinct from product read) |
mapping:read | Read ERP field-mappings, lint columns, and fetch the ERP template |
mapping:write | Create, update, and delete ERP field-mappings |
webhook:read | Read webhook endpoints and their delivery history |
webhook:write | Create, update, delete, and rotate webhook endpoints, and redeliver events |
There is no dpp:publish scope and no AI (ai.*) scope. Publication is a human action in the portal, and AI flows are portal-only. See Regulatory boundary.
Privileged scopes
Two scopes sit outside the grantable set above. They are minted only from the Company Portal admin-key dialog, each always in isolation, and cannot be requested through the normal scope picker:
| Scope | Grants |
|---|---|
keys:admin | Programmatic key management (/api/v1/keys — mint, list, rotate, revoke, IP allow-lists) |
gdpr:admin | GDPR data-subject endpoints (/api/v1/gdpr/*, live mode only) |
See Authentication → Privileged scopes.
Scope required per endpoint
| Endpoint | Scope |
|---|---|
GET /api/v1/industries | category:read |
GET /api/v1/categories | category:read |
GET /api/v1/categories/{code}/schema | schema:read |
GET /api/v1/categories/{code}/diff | schema:read |
GET /api/v1/suppliers | supplier:read |
GET /api/v1/products | dpp:read |
POST /api/v1/products | dpp:draft:write |
GET /api/v1/products/{id} | dpp:read |
PATCH /api/v1/products/{id} | dpp:draft:write |
PUT /api/v1/products/{id}/form-data | dpp:draft:write |
GET /api/v1/products/{id}/traceability | dpp:read |
POST /api/v1/products/{id}/traceability | dpp:draft:write |
GET /api/v1/products/{id}/components | dpp:read |
POST /api/v1/products/{id}/components | dpp:draft:write |
GET /api/v1/products/{id}/gate | dpp:gate:read |
GET /api/v1/products/{id}/readiness | dpp:gate:read |
GET /api/v1/products/{id}/schema-compatibility | dpp:read |
POST /api/v1/products/{id}/submit | dpp:submit |
POST /api/v1/products/{id}/withdraw | dpp:withdraw |
POST /api/v1/products/{id}/documents | document:write |
GET /api/v1/products/{id}/documents | dpp:read |
GET /api/v1/documents/{id}/download | dpp:read |
GET /api/v1/companies/me/gs1-prefixes | dpp:read |
POST /api/v1/companies/me/gs1-prefixes | dpp:draft:write |
GET /api/v1/categories/{code}/diff | schema:read |
GET /api/v1/erp-mappings | mapping:read |
POST /api/v1/erp-mappings | mapping:write |
PATCH /api/v1/erp-mappings/{id} | mapping:write |
DELETE /api/v1/erp-mappings/{id} | mapping:write |
POST /api/v1/mappings/lint | mapping:read |
GET /api/v1/categories/{code}/erp-template | mapping:read |
GET /api/v1/webhook-endpoints | webhook:read |
POST /api/v1/webhook-endpoints | webhook:write |
GET /api/v1/webhook-endpoints/{id} | webhook:read |
PATCH /api/v1/webhook-endpoints/{id} | webhook:write |
DELETE /api/v1/webhook-endpoints/{id} | webhook:write |
POST /api/v1/webhook-endpoints/{id}/rotate-secret | webhook:write |
GET /api/v1/webhook-endpoints/{id}/deliveries | webhook:read |
POST /api/v1/webhook-deliveries/{id}/redeliver | webhook:write |
POST and GET on /api/v1/keys (and /keys/{id}, /rotate, /bulk-revoke) | keys:admin (privileged) |
/api/v1/gdpr/access, /api/v1/gdpr/erase, /api/v1/gdpr/portability | gdpr:admin (privileged) |
The operational endpoints carry no scope: GET /api/v1/health and GET /api/v1/openapi.json are unauthenticated, and GET /api/v1/meta requires a valid key but no specific scope (it returns only the key's own grants and public constants).
Choosing scopes
Grant only what an integration needs. A read-only sync that mirrors product status into an ERP needs only dpp:read. A full authoring integration needs the discovery scopes (category:read, schema:read), the write scopes (dpp:draft:write, document:write), the gate scope (dpp:gate:read), and dpp:submit. Add dpp:withdraw only to the integration that is allowed to pull a published passport, and supplier:read only where you reference suppliers in traceability. Mint a separate key per integration so a revocation is contained.