0Bit Documentation

Execute against a 0Pools quote

POST /pools/{id}/transact - Execute once against an accepted quote.

0Pools API pages are for approved headless partners. They cover the partner-visible quote, transact, status, trade, and balance lifecycle only.

Endpoint

FieldValue
MethodPOST
Path/pools/{id}/transact
AreaTrades
Operation idtransact
Auth boundarySecret key from your server.

Use it for

Execute once against an accepted quote. Call this with the quoteId returned by a firm quote to reserve the trade, then poll for settlement. The endpoint requires your secret key and an entitlement to the pool in the path; a key scoped to another partner resolves as 404, never 403.

Use this endpoint only for the partner-scoped resource it describes. Store your own reference id, the returned 0Bit object id, the request id, timestamps, and the current status so support and reconciliation do not depend on browser callbacks alone.

Production rules

  • Keep secret keys on your server.
  • Validate environment, mode, entitlement, asset, network, and amount before the call.
  • Send an Idempotency-Key for the logical write and retry timeouts with the same key.
  • Branch on machine-readable status, error code, object id, and request id.
  • Treat examples and placeholder ids as fake data only.

Request

The body carries a single field: the quoteId from a firm quote. Everything else about the trade — side, pool, amounts, rate, and the on-ramp delivery address — is read from the locked quote, never from this body.

FieldInRequiredNotes
idpathYesPool id you are entitled to. Cross-tenant ids resolve 404.
quoteIdbodyYesA firm, unexpired, unconsumed quote id from POST /pools/{id}/quote.

Idempotency-Key is required

Transact is a money write, so the Idempotency-Key header is mandatory. Transact is also idempotent on the quoteId itself: re-posting the same quote returns the same trade rather than creating a second one. If you reuse an Idempotency-Key with a different body, the request is rejected as 409 (idempotency-conflict). Retry timeouts with the same key and the same body.

On-ramp delivery is locked at quote time

For a buy (on_ramp), the destination address and optional network are captured and validated when you created the quote, then copied onto the trade at transact — from the quote, never from this request body. You cannot redirect delivery here.

curl -X POST https://pools-api.0bit.app/v1/pools/pool_test_123/transact \
  -H "Authorization: Bearer sk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 00000000-0000-4000-8000-000000000123" \
  -d '{
    "quoteId": "quote_test_123"
  }'

Response

A successful call reserves the trade and returns a lean acknowledgement with status: "reserved". This response carries no amounts, rate, or bps fields — poll the status endpoint for lifecycle, and retrieve the trade for amounts and fees.

{
  "transactId": "txn_test_456",
  "status": "reserved",
  "quoteId": "quote_test_123",
  "idempotent": false
}
FieldTypeNotes
transactIdstringThe created (or existing) trade id. Persist it.
statusstringTrade status; reserved on a fresh execution. See the lifecycle below.
quoteIdstringThe quote this trade was executed against.
idempotentbooleantrue when a re-POST of the same quoteId returned the existing trade rather than creating a new one; false on first execution.

Persist transactId and quoteId. Use either to look the trade up later, poll Get 0Pools transaction status by quoteId for settlement and amounts, and retrieve the trade for amounts, rate, and fees.

Trade lifecycle

A reserved trade is not final. Progress it by polling Get 0Pools transaction status — that poll is the intended, idempotent way to advance a reserved trade toward a terminal state.

StatusMeaning
quotedQuote locked; not yet executed.
reservedTrade accepted and reserved by transact. Poll to progress.
settledTerminal success. Delivery completed for the trade.
failedTerminal failure.
releasedTerminal release; the reservation was unwound.
returnedA previously settled trade later reversed downstream, with an automatic refund to your pool balance. Surfaced via the status poll only — there is no webhook for it.

Terminal webhooks

When configured, 0Pools emits pool.transaction.settled, pool.transaction.failed, and pool.transaction.released. There is no webhook for returned — detect it by polling the status.

Errors

Errors use the unified envelope { type, code, message, request_id, doc_url, statusCode }, and every response carries an X-Request-Id header. Branch on the machine-readable code.

StatustypeTypical codeWhen it happens
400invalid_requestvalidationMissing or malformed quoteId.
401unauthorizedauthMissing or invalid secret key.
402invalid_requestinsufficient balancePre-funded balance cannot cover the trade.
403forbiddenpools_not_enabled, pool_access_suspended, kyc_not_approved, pool_not_allowed, key_mode_mismatchAccess denial. Branch on code.
404not_foundnot_found (no custom code; falls back to type)Unknown pool or quote, or a cross-tenant id.
409conflictexpired, consumed, reservation-failed, idempotency-conflictQuote expired or already consumed, the reservation could not be taken, or an Idempotency-Key was reused with a different body.
429rate_limitedvelocity capPer-partner rolling 24h notional velocity cap exceeded (configured per partner).
501server_erroroff_ramp not availableoff_ramp is not yet publicly enabled for this surface.

Sell (off_ramp) is gated

Executing an off_ramp quote deny-closes with 501 ("off_ramp is not yet available") unless the surface is enabled for your partner. Treat sell flows as gated early access until your account review confirms otherwise.

Public boundary

This reference covers partner-visible discovery, quote, transact, status, trade, and balance behavior. Liquidity operations, routing internals, provider details, reserve logic, and runbooks are outside the public API contract.

On this page