Skip to main content
A transfer moves a unit from its current owner to a new owner. Like a claim, it is authorized by a session token from a recent tap of the unit’s chip — an ownership change always traces back to physical possession of the item. The difference is who executes it. A claim hands a unit to its first owner, and Endstate can submit that for you. A transfer moves a unit between owners, and only the current owner can complete it: the API returns a ready-to-send transaction, and the owner submits it from their own wallet. Endstate authorizes the transfer but never moves a unit on an owner’s behalf.

How transferring works

1

Verify a tap

The user taps the item’s chip; your server verifies it (POST /v1/chips/ {chip_id}) and receives a session token scoped to that chip and unit.
2

Create the transfer

Call POST /v1/units/{unit_id}/transfers with the recipient’s to address, authorized by the session token. Endstate signs the transfer authorization and returns a transaction payload.
3

The current owner submits

The unit’s current owner sends the transaction from their wallet, on the collection’s network. A transaction sent by any other account is rejected.
4

Poll for status

Poll GET /v1/units/{unit_id}/transfers/{transfer_id} until status is confirmed.

Authorization

POST /v1/units/{unit_id}/transfers requires a session token (end_sess_...) from a tap of the same unit’s chip — an API key alone cannot create a transfer. GET /v1/units/{unit_id}/transfers/{transfer_id} accepts either an API key or a session token, so your backend or your client can poll for status.

Create a transfer

POST /v1/units/{unit_id}/transfers
to
string
required
The address that will receive the unit (0x followed by 40 hex characters).
The transaction payload is returned when the transfer is created. Hand it to the unit’s current owner (the from address) to submit.

Submitting the transaction

Unlike a claim’s client_broadcast submission — which any funded account can send — a transfer must be sent by the current owner: the transaction is rejected unless the sender is the from address. The owner needs gas on the collection’s network.
Use any EVM wallet client or signer — see the viem or ethers docs. In a user-facing flow this is typically the owner confirming in their connected or embedded wallet. The signed authorization is valid for 30 minutes from transfer creation — a transfer that is not submitted and confirmed in that window becomes expired.

Transfer status

GET /v1/units/{unit_id}/transfers/{transfer_id} — poll this until the transfer settles.

The transfer object

id
string (UUID)
The transfer’s unique identifier.
unit_id
string (UUID)
The unit being transferred.
to
string
The recipient address.
from
string
The current owner’s address — the only account that can submit the transaction.
status
string
One of prepared, confirmed, expired, failed.
transaction
object | null
The payload the current owner submits — to (the collection’s contract address) and data (the encoded transfer call). Returned when the transfer is created.
created_at
string (ISO-8601)
When the transfer was created.

Errors

Branch on error.code, not the HTTP status. See Errors for the full envelope.

Next steps

Claims

Hand a unit to its first owner — Endstate can submit that one for you.

Session tokens

How a tap produces the session token that authorizes a transfer.