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
| Field | Value |
|---|---|
| Method | POST |
| Path | /pools/{id}/transact |
| Area | Trades |
| Operation id | transact |
| Auth boundary | Secret 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-Keyfor 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.
| Field | In | Required | Notes |
|---|---|---|---|
id | path | Yes | Pool id you are entitled to. Cross-tenant ids resolve 404. |
quoteId | body | Yes | A 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
}| Field | Type | Notes |
|---|---|---|
transactId | string | The created (or existing) trade id. Persist it. |
status | string | Trade status; reserved on a fresh execution. See the lifecycle below. |
quoteId | string | The quote this trade was executed against. |
idempotent | boolean | true 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.
| Status | Meaning |
|---|---|
quoted | Quote locked; not yet executed. |
reserved | Trade accepted and reserved by transact. Poll to progress. |
settled | Terminal success. Delivery completed for the trade. |
failed | Terminal failure. |
released | Terminal release; the reservation was unwound. |
returned | A 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.
| Status | type | Typical code | When it happens |
|---|---|---|---|
400 | invalid_request | validation | Missing or malformed quoteId. |
401 | unauthorized | auth | Missing or invalid secret key. |
402 | invalid_request | insufficient balance | Pre-funded balance cannot cover the trade. |
403 | forbidden | pools_not_enabled, pool_access_suspended, kyc_not_approved, pool_not_allowed, key_mode_mismatch | Access denial. Branch on code. |
404 | not_found | not_found (no custom code; falls back to type) | Unknown pool or quote, or a cross-tenant id. |
409 | conflict | expired, consumed, reservation-failed, idempotency-conflict | Quote expired or already consumed, the reservation could not be taken, or an Idempotency-Key was reused with a different body. |
429 | rate_limited | velocity cap | Per-partner rolling 24h notional velocity cap exceeded (configured per partner). |
501 | server_error | off_ramp not available | off_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.