Response examples
The Integration API (Phase 1 core and Phase 1.5 endpoints) is GA on production (app.traceable.digital) as of v0.99.6.1. See Availability.
Concrete success-response examples for each endpoint, to sit alongside the schema-driven API reference. Shapes come from the OpenAPI spec; examples marked verified carry counts observed during an end-to-end walk. IDs and field values that are not marked verified are representative.
A few live responses currently carry slightly more than the spec schema lists. These examples show the live shape:
GET .../gateandGET .../readinessincludeversionandproductId.POST /productsnestspublicUrlinsideproduct.- The gap
resolvervalue can besupportin addition toerp,operator-ui, andverifier.
These are known spec-versus-implementation deltas to tighten in a future spec pass, not new behaviour. The rendered reference reflects the spec.
Discovery
GET /api/v1/industries — 200 (verified: count = 1 for a single-industry tenant)
{ "industries": [ { "code": "BAT", "name": "Batteries" } ], "count": 1 }
GET /api/v1/categories — 200 (verified: count = 7)
{
"categories": [
{ "code": "BAT-EV-001", "name": "EV Battery", "industryCode": "BAT",
"schemaVersion": "3", "schemaHash": "3@2026-02-18T00:00:00.000Z" }
],
"count": 7
}
GET /api/v1/categories/{code}/schema — 200 (verified: field_count 118, required_count 66 for BAT-STAT-001). See Category schema formats for the ?format= representations.
{
"schema": {
"category_code": "BAT-EV-001", "schema_version": "3",
"field_count": 118, "required_count": 66,
"fields": [
{ "field_id": "nominal_voltage", "label": "Nominal Voltage", "type": "float", "required": true,
"ui_section": "Electrical", "access_tier": "public", "help_text": "Rated nominal voltage.",
"validation": { "min": 0, "max": 1000 }, "options": null, "unit": "V", "example": 3.7 },
{ "field_id": "battery_status", "label": "Battery Status", "type": "select", "required": false,
"ui_section": "General", "access_tier": "public", "help_text": null, "validation": null,
"options": ["Original", "Repurposed", "Remanufactured"], "unit": null, "unit_unspecified": true,
"example": null, "example_unspecified": true }
]
}
}
GET /api/v1/suppliers — 200 (verified: count = 13, source = invited, PII-free)
{
"suppliers": [
{ "id": "52065fd9-e6ac-49a9-af95-4bf9e3886451", "legalEntityName": "SQM S.A.",
"vatNumber": "CL-99999999", "countryOfRegistration": "CL", "entityType": "Supplier",
"onboardingStatus": "Active", "source": "invited" }
],
"count": 13
}
The supplier set never includes contactEmail, phoneNumber, or primaryAddress (GDPR data minimisation). source is one of invited, traceability, or membership.
Products and authoring
GET /api/v1/products — 200
{
"products": [
{ "id": "…", "name": "UltraCell EV-900", "status": "Draft", "publicUrlSlug": "ultracell-ev-900",
"dppId": "DPP-…", "createdAt": "2026-06-29T…Z", "updatedAt": "2026-06-29T…Z" }
],
"count": 1, "nextCursor": null
}
POST /api/v1/products — 201
{
"product": {
"id": "…", "slug": "ultracell-ev-900", "status": "Draft",
"publicUrl": "https://app.traceable.digital/dpp/ultracell-ev-900"
}
}
PATCH /api/v1/products/{id} — 200
{ "product": { "id": "…", "version": "1.0", "lockVersion": 4 }, "lockVersion": 4 }
PUT /api/v1/products/{id}/form-data — 200 (verified shape; fieldErrors: [] is the accept signal)
{
"product": { "id": "…", "version": "1.0", "lockVersion": 4, "status": "Draft",
"completenessScore": 8, "sustainabilityScore": 0 },
"fieldErrors": [],
"readiness": { "ok": false, "gapCount": 40 }
}
Traceability and components
GET /api/v1/products/{id}/traceability — 200
{
"traceability": [
{ "id": "…", "materialName": "Lithium Carbonate", "materialType": "Raw", "tier": 1,
"supplierId": "52065fd9-…", "originCountry": "CL", "quantity": 5, "quantityUnit": "kg",
"complianceStatus": "Pending", "parentTraceId": null, "linkedDppId": null }
],
"count": 1
}
POST /api/v1/products/{id}/traceability — 201 (verified: supplierLinkageBasis = invited)
{ "traceability": { "id": "9844ce67-…", "supplierLinkageBasis": "invited" } }
GET /api/v1/products/{id}/components — 200
{
"components": [
{ "id": "…", "componentName": "BMS Module", "componentType": "Electronics", "quantity": 1,
"childProductId": null, "childDppUrl": null, "childDppId": null, "childProductSlug": null }
],
"count": 1
}
POST /api/v1/products/{id}/components — 201
{ "component": { "id": "133f86de-…" } }
Lifecycle and gates
GET /api/v1/products/{id}/gate — 200
{
"version": "1.0", "productId": "…", "ready": false, "gapCount": 42,
"gaps": [
{ "kind": "espr_mandated_missing", "message": "Required field \"Nominal Voltage\" is empty.",
"resolver": "erp", "detail": { "field": "nominal_voltage", "label": "Nominal Voltage" } }
]
}
GET /api/v1/products/{id}/readiness — 200 (verified: completenessScore 9, gapCount 42, ok false)
{
"version": "1.0", "productId": "…", "status": "Draft", "ok": false, "publishReady": false,
"completenessScore": 9, "requiredFields": { "filled": 6, "total": 66 },
"complianceScore": 9, "complianceStatus": "AtRisk",
"sections": {
"formFields": 40,
"documents": { "uploaded": 1, "requiredCount": 2, "verifiedCount": 0, "pendingCount": 1, "expiredCount": 0 },
"traceability": { "rows": 0, "requiresTraceability": true, "score": 0 },
"identity": "not_configured"
},
"missingFieldEntries": [
{ "fieldId": "nominal_voltage", "label": "Nominal Voltage", "kind": "espr_mandated_missing" }
],
"gapCount": 42,
"gaps": [
{ "kind": "espr_mandated_missing", "message": "Required field \"Nominal Voltage\" is empty.",
"resolver": "erp", "detail": { "field": "nominal_voltage", "label": "Nominal Voltage" } }
]
}
ok === true means the product is ready to submit. A gap's resolver is one of erp, operator-ui, verifier, or support: only erp gaps are ones your integration can close.
POST /api/v1/products/{id}/submit — 200
{ "product": { "id": "…", "status": "Submitted" } }
POST /api/v1/products/{id}/withdraw — 200 (on an Approved product; the public DPP then returns HTTP 410)
{ "product": { "id": "…", "status": "Withdrawn" } }
Documents
GET /api/v1/products/{id}/documents — 200 (verified: count = 4)
{
"documents": [
{ "id": "…", "name": "EC Declaration of Conformity", "type": "Certificate", "fileName": "ec-doc.pdf",
"fileSha256": "9f86d0…", "virusScanStatus": "Clean", "verificationStatus": "Verified",
"validUntil": null, "createdAt": "2026-03-01T…Z" }
],
"count": 4
}
POST /api/v1/products/{id}/documents — 201 (multipart upload)
{ "document": { "id": "…", "productId": "…", "status": "Pending" } }
GET /api/v1/documents/{id}/download — 200
{ "download": { "url": "https://…/sign/…", "expiresInSeconds": 7200 } }
The download URL is a short-lived signed URL (two hours).
Company (GS1 prefixes)
GET /api/v1/companies/me/gs1-prefixes — 200
{ "prefixes": ["0614141"] }
POST /api/v1/companies/me/gs1-prefixes — 201 (add semantics; returns the resulting set)
{ "prefixes": ["0614141"] }
For error responses, see Errors and conventions.