Skip to main content
The Quickstart covers the same verification flow using a simulated test chip. This guide is for production: real encoded NFC hardware, real items, and the decisions your integration needs to make at each stage. Read through it once before writing code — the “why” at each step shapes your architecture.
1

Set up and pair your items

Before any taps, each item needs a collection, its own unit, and a paired chip — a one-time setup you run during fulfillment, not at tap time. The Set up your catalog guide covers it end to end: create the collection, create a unit per item, and pair the encoded chip (which issues the unit and assigns its serial).Two things to carry into the verify flow below:
  • On every tap, the chip’s tap URL carries the chip_id and a fresh one-time e. The e you supplied at pairing comes from encoding and is consumed then — it is not the tap e.
  • A chip is permanently bound to its unit. A chip can also be paired the first time it is tapped, through a hosted pairing screen — see Pairing a chip, including how to route unpaired taps to your own system.
2

The user taps

When a user taps the NFC chip on the item, the chip generates a fresh one-time e value and opens a URL. That URL carries both the chip_id and the new e as parameters — for example:
The format of this URL is fixed by the chip’s encoding. Your job is to extract chip_id and e from it (or from the query parameters your app receives when the user is redirected to your domain) and forward them to your backend immediately.The e value is a credential. Treat it accordingly:
  • Do not log it.
  • Do not store it at rest.
  • Pass it to the verify endpoint promptly — it is single-use and has a short validity window.
Your frontend does not call the Endstate API directly. It sends chip_id and e to your own server, which makes the verify call using your API key.
Reading the tap in a browser is non-trivial. Endstate ships no browser NFC SDK — the tag is standard NFC. Web NFC (NDEFReader) works only on Android Chrome (secure context + user gesture); desktop needs a USB/PC-SC reader driven over WebUSB/WebHID; iOS Safari has no in-browser tap path. The tag’s payload is just a URL — read the NDEF record, extract the URI, then pull chip_id + e from it.
A raw tap URL is often a short Endstate URL that 302-redirects to the destination, with the params on the redirect target. If you resolve a tap URL server-side, follow the redirect and read the params off the Location header — fetch(url, { redirect: "manual" }).
3

Verify the tap

Your backend calls POST /v1/chips/{chip_id} with the e value. This is the core verification call. A successful response is non-replayable proof that the physical chip was tapped moments ago.
Here is what each field tells you:id — the unique scan record ID. Log it alongside the X-Request-Id response header for support requests.chip.scan_count — the total number of successful verifications for this chip, including this one. A value of 1 means this is the first successful tap. Higher values mean the item has been verified before. Your application decides what a high count means — Endstate records the count but does not set policy on it.unit — the digital record for the verified item, including any name, external_id, and attributes you set when creating it. Issuance status and serial number are nested under unit.collection.tokenstatus: "active" and a serial value confirm the unit is fully issued. Use this data to render an item detail view.session_token — an object containing a short-lived token string (end_sess_...) that proves this specific chip was just tapped. Valid for 600 seconds by default. Pass the token string to a browser or mobile client. See the next step.redirect_url — the canonical page to send the user to after a successful tap, or null if your account has no verified domain configured. When present, you can redirect the user here directly; the URL already carries the context needed to display the correct item.
4

Hand the session token to your client

The session_token from the verify response is designed to cross the server–client boundary safely. Your API key (end_sk_...) must never leave your server — but the session token can.After verifying the tap server-side, pass the token string from the session_token object to your browser or mobile client. The client can then call GET /v1/session-tokens/current using that token string as its bearer credential:
This tells the client exactly which chip was tapped, which unit it belongs to, and when the session expires. The client can use this to confirm scope without a second round-trip to your server.The default session lifetime is 600 seconds. If your user flow needs more or less time, pass ttl (integer, 60–3600) in the verify request body.See Session tokens for the full security model and use cases.

Handle failures

Branch on error.code — never on HTTP status or message. The three codes specific to chip verification are: For the full error envelope and handling conventions, see Errors.
Use dry_run: true in the verify request body to check that a chip decodes correctly without recording a scan or issuing a session token. This is useful during QA to confirm a newly encoded chip is readable before releasing the item. A dry_run verify call counts neither toward scan_count nor toward any quota.
The session token also authorizes claiming a unit on behalf of the user who tapped — handing it to a recipient. See Claims for the full flow, execution modes, and status polling.

Next steps

Claim a unit

Transfer ownership of the verified unit to a recipient, authorized by the session token.

Session tokens

Understand the credential you just received and how to use it on a client.