Developer Docs
Sandbox · API v2PlaygroundGet API keys

Products & categories

A Product is a tenant-owned commercial aggregate, not automatically a passport. Its category selects a schema that governs the extensions payload you must supply.

Fields

  • id, assigned on create; capture it as {{productId}}.
  • gtin, serialNumber, the GS1 identity used later for Digital Link resolution.
  • category, resolves to an effective schema; drives extensions.
  • extensions, category-specific fields (electronics ≈ 8, battery ≈ 30).

Serial rules

  • Serial must be ≤ 20 characters; 21 → 400 VALIDATION_ERROR naming the SGTIN-96 bound. Unprintable serials are rejected before anything irreversible.
  • Numeric serials must fit the EPC carrier bound (≤ 238) for carrier/EPC generation; alphanumeric serials may still yield a valid Digital Link/QR while EPC carrier generation refuses, a deliberate split.

Lifecycle

A product moves draft → validated → ready_for_dpp via POST /products/{id}/lifecycle/transition. Two transitions are required before a passport can be generated. Illegal transitions are typed errors, not silent no-ops. Every change emits a domain event (product.created, product.updated).

Create guardrails

ConditionResponse
description < 10 chars400 VALIDATION_ERROR
Missing extensions422 EXTENSIONS_REQUIRED
Category has no effective schema422 SCHEMA_NOT_FOUND
Serial > 20 chars400 VALIDATION_ERROR (SGTIN-96)
Bad GS1 check digit / non-digit GTIN400 VALIDATION_ERROR, a valid GTIN-8/12/13 is normalized to 14 digits at write time
Update verbPATCH /products/{id} (canonical) or PUT (alias), both run the same full-update handler

Categories & schemas

GET /products/categories lists the tenant's categories with usage counts. Schema discovery lives on the dedicated surface:

HTTP
GET /api/v2/schemas/categories
    → every category with its schema versions, jurisdiction, effective window, effectiveNow flag

GET /api/v2/schemas/categories/{category}?jurisdiction=EU&date=2026-07-22T00:00:00Z
    → the schema effective AT that instant for that jurisdiction: schemaId, schemaRegistryId,
      version, effectiveFrom/Until, full fieldSchema, and _links.importTemplate
      (append &includeFields=false to skip the field definitions)

GET /api/v2/schemas/categories/{category}/import-template?jurisdiction=EU
    → text/csv "import-template-{category}-v{version}.csv" rendered from that SAME schema

Resolution uses the identical effective-schema lookup the create and import paths run, what discovery returns is what validation enforces. A category is only usable when its schema row has a non-null effective_from.

Advertised ≠ resolvable. Category hint lists in error responses can name categories such as dpp and vehicle whose schema rows have effective_from = NULL, they then fail on create with 422 SCHEMA_NOT_FOUND. Don't trust a hint blindly; confirm against a successful create. (Tracked, status Partial.)
Was this page helpful?
Thanks, noted.Feedback goes to the docs team by email.