Skip to main content
This is the setup you do before items reach customers — in your back office or fulfillment pipeline, once per product line and once per item. By the end, each physical item has a unit and a paired chip, and is ready to verify at a tap. The Quickstart runs this same flow with a simulated test chip. This guide is the production version: real encoded chips, real items, at scale.
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. POST /v1/collections returns immediately with contract.status: "deploying" — provisioning is asynchronous.
The collection is returned at the top level (as are all single-resource responses — see Response shape). Poll 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 from name when omitted.
  • chain_id — override the default network. The value must be one of the networks enabled for your organization (allowed_chain_ids from GET /v1/settings); otherwise the API returns validation.failed.
2

Create a unit for each item

A unit is the digital record of one physical item. Create one per item with POST /v1/units, using the active collection’s id.
The unit starts with 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 (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.
The chip_id and e come from your chip encoding process — do not derive them from a tap URL. The encoding e is consumed on pairing, and a fresh one-time e is produced at each user tap.
Test chip (development). Pass 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.
Pairing returns the assigned 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.