Skip to content

Authentication

Public market data needs no authentication. To manage orders or complete a trade, sign a message with your Bitcoin address and exchange the signature for a session token. There are no API keys or minimum-balance login requirements.

Set $API as shown in the quickstart.

Terminal window
curl -sS -X POST "$API/v1/auth/challenge" \
-H 'Content-Type: application/json' \
-d '{"address":"YOUR_BITCOIN_ADDRESS"}'

Save result.message and result.issued_at. The challenge expires after 10 minutes. Sign the exact message, including its newlines, using your wallet or local signer.

Terminal window
curl -sS -X POST "$API/v1/auth/verify" \
-H 'Content-Type: application/json' \
-d '{
"address":"YOUR_BITCOIN_ADDRESS",
"issued_at":1790000000,
"signature":"YOUR_BASE64_MESSAGE_SIGNATURE"
}'

Replace the example timestamp with the challenge’s issued_at. Successful verification returns result.token, result.address, and result.expires_at. The token lasts 24 hours.

Terminal window
export TOKEN='YOUR_SESSION_TOKEN'
curl -sS "$API/v1/address/YOUR_BITCOIN_ADDRESS/events?limit=20" \
-H "x-session-token: $TOKEN"

When the token expires, authenticate again as the same address. If a trading request was interrupted, read its status before resuming.

The default is BIP-322. A signer returning a BIP-137 recoverable signature must declare the format in the verification request:

{
"verification": {
"method": "BIP-137",
"format": "legacy_recoverable"
}
}

P2PKH (1…) addresses also accept a classic 65-byte recoverable signature or a two-item signature/public-key stack. A wrong key, changed message, or expired challenge returns 401 signature_invalid.

Browser integrations with a compatible connect-time proof can use POST /v1/auth/session. That proof must be at most five minutes old and bound to an allowed site origin. Use the challenge flow for bots and other integrations.

The public TypeScript SDK is coming soon. The HTTP flow above works today with your wallet or local message signer.

A session authorizes private reads and account actions for one address, including delisting, cancelling orders, and eligible artist-profile edits. It does not authorize spending: trades still require Bitcoin transaction signatures.

Tokens have no read-only or per-action scopes and cannot be revoked individually. Disconnecting removes the local token; it does not invalidate copies. Creating a new session does not invalidate earlier sessions.

For a bot, use a dedicated trading address, keep its token separate from its signer, and renew the session as needed. Public analytics does not need a token. Do not share a portfolio token with a service that only needs public data.

The address that signs a trade does not have to store your collection.

Role Purpose Supported address families
Signing Funds purchases and signs listings, offers, and acceptances P2WPKH (bc1q…), P2TR (bc1p…)
Delivery Receives a detached purchased balance Any family supported by Counterparty
Source Supplies a balance when preparing a unit for sale P2PKH (1…), P2WPKH, P2TR

Authentication and transaction signing are separate checks. An address can authenticate yet be unsupported for a particular trading role; that request returns 400 unsupported_address_type.

  • Checkout uses delivery_address, defaulting to buyer_address.
  • Exact offers use receive_address.
  • Collection-offer preflights use delivery_address, defaulting to the session address.

Detached delivery can go to a legacy address. Attached delivery returns the asset UTXO to the trading address; see Buying.

POST /v1/listing-preflights and POST /v1/attach-templates accept asset_source separately from the seller or utxo_owner. The source signs its own attach inputs; the prepared unit belongs to the SegWit seller, who signs the listing. Use the seller’s session for this workflow.

With a P2PKH source, signing can change the attach transaction ID. Read the final asset outpoint from the preflight after submission rather than predicting it from the unsigned transaction.