Order matters, and the API enforces it: a collection must finish provisioning
(
active) before you can add units, and pairing a chip is what issues the
unit — it assigns the serial and flips the unit from pending to active.1
Create a collection
A collection is a product line or drop. Create it once; every unit inside it shares its serial-number namespace. The collection is returned at the top level (as are all single-resource responses — see Response shape). Poll
POST /v1/collections returns immediately with contract.status: "deploying" — provisioning is asynchronous.GET /v1/collections/{collection_id} until contract.status is "active" before adding units (a short interval; give up after a reasonable timeout). Creating a unit too early returns collection.not_active.Only name is required — the collection is created on your organization’s default network, configured by Endstate at onboarding. The optional fields:external_id— your own identifier for the collection, unique within your organization. The example above sets it so a re-run can look the collection up instead of creating a duplicate (see Re-runs and conflicts).redirect_url— where a tap on any of this collection’s items sends the user. See Tap redirects.symbol— a short display symbol, 1–4 characters. Derived fromnamewhen omitted.chain_id— override the default network. The value must be one of the networks enabled for your organization (allowed_chain_idsfromGET /v1/settings); otherwise the API returnsvalidation.failed.
2
Create a unit for each item
A unit is the digital record of one physical item. Create one per item with The unit starts with
POST /v1/units, using the active collection’s id.token.status: "pending" and no serial — both are set when you pair a chip in the next step.Set external_id to your own SKU or serial so you can look the unit up later (GET /v1/units?external_id=...) without storing Endstate IDs. There’s no bulk endpoint — loop the call to register every item ahead of time, before they ship.3
Pair a chip (this issues the unit)
Pairing links a specific NFC chip to a specific unit and triggers issuance. A chip is permanently bound to one unit — it cannot be re-paired (Test chip (development). Pass Pairing returns the assigned
chip.already_paired). If a paired chip is damaged before the item ships, don’t re-pair it — replace the unit’s chip with a fresh one.Encoded chip (production). Do this during fulfillment, after the chip is encoded and before the item ships. Supply the chip_id (10 uppercase hex characters) and e (32 uppercase hex characters) from your encoding step.is_test: true and Endstate assigns the chip_id — no hardware needed. Generate tap values for it later with POST /v1/chips/{chip_id}/tap. See Test without hardware.chip_id at the top level (the value to use for a test chip’s taps). Issuance is asynchronous and has no failure status — poll GET /v1/units/{unit_id} until collection.token.status is "active"; the unit’s collection.token.serial is set at that point. If it stays pending well beyond a few minutes, stop polling and contact support.Re-runs and conflicts
external_id makes setup safe to re-run. If a step reports a conflict, look the existing resource up by its external_id and reuse it instead of creating a duplicate.
Branch on error.code (never the message or HTTP status):
See Errors for the full envelope.
Next steps
Verify a unit
Now that items are registered, handle the user tap and verify authenticity
end to end.
Tap redirects
Send users to your own page after a tap — per unit, per collection, or
org-wide.

