Skip to main content
While the v1 API is in early access, breaking changes may occasionally ship in place. Every one is recorded here with what changed and how to migrate.
July 2026

Transfer a unit between owners

A unit can now move from its current owner to a new owner — tap-authorized like a claim, but executed by the current owner:
  • POST /v1/units/{unit_id}/transfers (session token required) creates the transfer and returns a transaction payload. Only the unit’s current owner can submit it — Endstate authorizes the transfer but never relays it, and the authorization is valid for 30 minutes.
  • GET /v1/units/{unit_id}/transfers/{transfer_id} (API key or session token) polls status: preparedconfirmed; expired and failed are terminal.
  • New error codes: transfer.in_progress, transfer.already_to_recipient, transfer.owner_unknown, transfer.not_found.
See the new Transfers concept page.

Replace a unit’s chip

Until a unit is claimed, its chip can now be replaced — for example when a chip is damaged or fails QA after pairing but before the item reaches its owner:
  • POST /v1/units/{unit_id}/chip-replacements pairs an existing, unpaired chip to the unit and retires the current one. Endstate performs the swap; replacement is asynchronous.
  • GET /v1/units/{unit_id}/chip-replacements/{replacement_id} polls status (preparedsubmittedconfirmed; failed is terminal).
  • New error codes: chip_replacement.locked (unit already claimed — the chip is permanently locked), chip_replacement.in_progress, and chip_replacement.not_found.
See Replacing a chip.

chain_id is now optional on collection create

Collections are created on your organization’s default network when chain_id is omitted — configured by Endstate at onboarding, so most integrations never need to send a network identifier. An explicit chain_id is still accepted as an override, but must be one of the networks enabled for your organization.GET /v1/settings now returns your network configuration alongside default_redirect_url: default_chain_id (the default network, or null if none is configured) and allowed_chain_ids (the networks an explicit chain_id may use). Both are read-only.No migration needed — requests that send chain_id keep working unchanged.

Single-resource responses are now top-level (breaking)

POST, GET, and PATCH responses for collections and units previously wrapped the resource in an envelope — { "collection": {...} } / { "unit": {...} }. They now return the resource’s fields at the top level, making every single-resource response uniform (chips, verify, session tokens, claims, and settings were already top-level).Migration: if your integration reads response.collection.id or response.unit.id from these endpoints, read the same fields at the top level instead (response.id, response.collection.token, …). List responses are unchanged — they still wrap under a plural key alongside pagination. See Response shape.

Empty request bodies accepted on all-optional POSTs

A POST with a JSON content-type and an empty body previously failed JSON parsing. An empty or whitespace-only body is now treated as {}, so requests like POST /v1/chips/{chip_id}/tap work without a body. Endpoints with required body fields still validate as before.
June 2026

Tap redirects

Control where a user lands after tapping a chip, at three levels — the most specific one wins:
  • redirect_url is a new (nullable, always-present) field on the unit and collection resources, accepted on create and editable via new PATCH /v1/units/{unit_id} and PATCH /v1/collections/{collection_id} endpoints.
  • New GET/PATCH /v1/settings endpoints manage the org-wide default_redirect_url.
  • Redirected taps carry endstate_chip_id and endstate_e query parameters so your page can verify the tap.
See Tap redirects.

Claims

Transfer on-chain ownership of a unit to a recipient wallet, authorized by the session token from a recent tap of that unit’s chip:
  • POST /v1/units/{unit_id}/claims creates a claim (endstate_relay or client_broadcast execution).
  • GET /v1/units/{unit_id}/claims/{claim_id} polls status (claimingclaimed / expired / failed).
See Claims and the Claim a unit guide.