0Bit Documentation

Pools API

Use the partner-safe 0Pools lifecycle for entitled pool discovery, short-lived quotes, one-time execution, trade status, funding, and reconciliation.

The Pools API is the headless liquidity surface for approved partners. Because 0Pools is partner-gated and liquidity-sensitive, the docs stay at the safe lifecycle level: discover entitled pools, read pool capabilities, request a short-lived quote, accept or reject it, transact once, poll or retrieve status, and reconcile partner-visible records.

This page deliberately does not publish treasury balances, provider names, reserve allocation, market-maker terms, routing strategy, bank rails, rebalancing thresholds, or internal risk controls.

0Pools is not the default public ramp

Use 0Gate for hosted buy/sell/swap UX. Use 0Pools only for approved headless partners that own their user experience and have the required compliance, entitlement, and operating model.

Gated early access

0Pools is in gated early access, provisioned per partner by 0Bit. It is not GA and not self-serve. Access, status, KYC state, tier, and allowed pools are operator-provisioned; you cannot grant them through the API.

What this page covers

  • The safe Pools API lifecycle, end to end.
  • Which calls use publishable keys and which require secret keys.
  • Quote (firm vs indicative), transact, reject, status, trade, capabilities, funding, balance, and ledger behavior.
  • Idempotency, HTTP status handling, velocity limits, and reconciliation rules.
  • What must stay out of browser code, customer-facing UI, and support artifacts.

Value types

Money is strings, bps are integers

Money and rate values are decimal strings (for example "100.00"), never floats. Basis-point fields (spreadBps, feeBps, totalBps, drawFeeBps, spreadCapBps) are integers. Preserve strings end to end so you never lose precision through a float round-trip.

Endpoint map

MethodPathAreaPurposeAuth boundary
GET/poolsDiscoveryList pools this partner is entitled to, with availability.Publishable or secret key, discovery only.
GET/pools/{id}/capabilitiesDiscoveryRead a pool's self-describing networks, sides, fees, and limits.Secret key, partner-scoped.
POST/pools/{id}/quoteQuotesLock a short-lived firm quote, or price an indicative one.Secret key, server-side.
POST/pools/{id}/transactTradesExecute once against a firm quote.Secret key, server-side, idempotent.
GET/pools/quotes/{quoteId}QuotesRetrieve a quote without consuming or extending it.Secret key, partner-scoped.
POST/pools/quotes/{quoteId}/rejectQuotesExplicitly decline a quote so it cannot be transacted.Secret key, server-side.
GET/pools/transactions/{quoteId}TradesPoll trade status by quote id (advances settlement).Secret key, partner-scoped.
GET/pools/tradesTradesList partner trades with cursor pagination and filters.Secret key, partner-scoped.
GET/pools/trades/{transactId}TradesRetrieve one trade (pure read, does not advance settlement).Secret key, partner-scoped.
GET/pools/{id}/balanceBalanceRead a partner-visible balance and entitlement view.Secret key, partner-scoped.
GET/pools/fundingFundingRead your dedicated deposit instructions for the fiat balance.Secret key, partner-scoped.
GET/pools/ledgerReconciliationList partner-scoped balance movements (credits/debits).Secret key, partner-scoped.

Key boundary

Publishable keys (pk_*) are discovery-only — they list entitled pools and nothing else. Every quote, transact, status, trade, balance, capability, funding, and ledger call requires a secret key (sk_*) used server-side only. All responses are partner-scoped; a request for another partner's object returns 404, never 403.

Lifecycle

The important production rule is that a firm quote is short-lived and single-use. Do not let the browser create or execute it directly. Your server should request the quote, store the quote id and expiry, show the user only the partner-visible economics, and execute once if the user accepts before expiry.

Trade status lifecycle

A trade moves through PoolTxnStatus values. Polling the status by quote id is the intended, idempotent way to advance a reserved transaction toward a terminal state.

StatusMeaning
quotedA firm quote exists but has not been transacted.
reservedtransact accepted the quote and reserved the trade; poll to progress it.
settledThe trade completed and was delivered.
releasedThe reservation was released without settling.
failedThe trade could not be completed.
returnedA previously settled trade was reversed (a payout returned by the receiving bank, or a delivery that failed downstream).

'returned' is poll-only and auto-refunds

A settled trade can later flip to returned. When that happens, the trade amount is auto-refunded to your partner balance with an idempotent, conflict-guarded credit (visible in GET /pools/ledger). There is no webhook for returned — surface it through the status poll, and reconcile the refund from the ledger.

Discovery

GET /pools returns the pools the partner is entitled to, including an availability signal. Treat that signal as current state, not a promise that every future quote will be available.

curl -X GET https://pools-api.0bit.app/v1/pools \
  -H "Authorization: Bearer pk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Use discovery to populate UI choices, cache lightly, and hide unavailable pools. Do not expose internal route details or fallback providers.

Capabilities

GET /pools/{id}/capabilities is self-describing. Read it instead of hard-coding networks, sides, fees, or order limits — these are configured per partner and per pool and can change.

curl -X GET https://pools-api.0bit.app/v1/pools/EUR-USDT/capabilities \
  -H "Authorization: Bearer sk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Capability fieldUse it for
poolId / pairIdentify the pool and currency pair.
fiatCurrency / cryptoCurrencyThe fiat and crypto legs supported by the pool.
supportedNetworksThe delivery networks you may pass as destNetwork (EVM-only).
sidesWhich sides (on_ramp / off_ramp) are enabled for this pool.
tierThe partner's tier for this pool.
drawFeeBps / spreadCapBpsApproved economic bounds (integers, basis points).
minOrderUsdt / maxOrderUsdtPer-order minimum and maximum.
cryptoDepositAddressYour dedicated 0Pools deposit address for off-ramp crypto receipts.

Quote

POST /pools/{id}/quote prices a specific conversion. A firm quote (the default) locks a short time-to-live quote when the route is available; an indicative quote is price-only.

curl -X POST https://pools-api.0bit.app/v1/pools/EUR-USDT/quote \
  -H "Authorization: Bearer sk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "side": "on_ramp",
    "fiatCurrency": "EUR",
    "cryptoCurrency": "USDT",
    "amount": "100.00",
    "type": "firm",
    "cryptoNetwork": "tron",
    "destAddress": "0xAbC0000000000000000000000000000000000123",
    "destNetwork": "arbitrum"
  }'

type selects the quote kind (firm or indicative, default firm). cryptoNetwork selects the crypto network for the quote (tron, ethereum, bsc, polygon, or solana, default tron); the on-ramp delivery network is set separately by destNetwork from the EVM-only allowlist.

Firm vs indicative quotes

A firm quote (default) is executable: it returns a quoteId, a short expiresAt (about 15 seconds), rate, spreadBps, feeBps, and executable: true. Only firm quotes can be transacted. An indicative quote is price-only: it returns no quoteId, executable: false, and is not persisted. Use it to preview pricing, never to transact.

Quote fieldUse it for
quoteIdJoin quote, trade, status, and reconciliation records (firm only).
executableDecide whether the UI can proceed to transact.
rateShow partner-visible pricing (output-per-input).
spreadBps / feeBpsShow approved economics where allowed.
expiresAtPrevent stale accept buttons (firm only).
available / unavailableReasonExplain why a route cannot be used without exposing internals.

Amount semantics. For on_ramp, amount is the fiat you pay in and the quoted rate (output-per-input) describes the crypto you receive. For off_ramp, amount is the crypto you sell and the quoted rate describes the fiat you receive. rate is always output-per-input; derive the opposite leg from amount × rate.

Unavailable routes fail soft, never lock

When a route cannot be priced, the quote returns HTTP 200 with available: false, an unavailableReason (pool_dry, engine_unavailable, or rate_unavailable), and no quoteId. There is nothing to transact — show the reason and let the user choose another route. Do not retry in a tight loop.

On-ramp delivery

For a buy (on_ramp), destAddress is required and is captured and locked at quote time — validated as an EVM 0x address (a mixed-case address must pass an EIP-55 checksum; all-lowercase or all-uppercase addresses pass on format), restricted to the EVM-only network allowlist. The address is copied onto the trade from the quote at transact (never re-read from the transact body) and delivered on settle. A missing destAddress on an on_ramp quote returns 400.

Delivery networks are EVM-only — for example, USDT on Arbitrum. Read the exact set from supportedNetworks in /capabilities rather than hard-coding it. The customer needs only an EVM wallet address; no wallet on any other chain is required.

Accept or reject

If the user accepts, call POST /pools/{id}/transact with the quote id from your server.

curl -X POST https://pools-api.0bit.app/v1/pools/EUR-USDT/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"
  }'

Idempotency-Key is required on transact

transact is a money write and requires an Idempotency-Key header. It is also idempotent on quoteId: a safe retry returns the existing trade rather than creating a duplicate. Reusing the same key with a different body returns 409. The quote call does not require an idempotency key.

A successful transact returns the reserved trade (transactId, quoteId, status: reserved, …); poll to progress it. Transact can fail with 402 (insufficient balance to reserve), 409 (quote expired, already consumed, or reservation failed), or 501 (off-ramp not enabled for this partner, pool, or environment — see below).

If the user declines, call POST /pools/quotes/{quoteId}/reject. Rejecting is money-free and sets the quote's status to rejected — a distinct terminal state from consumed, so a rejected quote can never be transacted. If a quote is already consumed, rejected, or expired, refresh state and branch instead of retrying blindly.

Derived quote status precedence

A quote's status is derived from activeconsumed | expired | rejected, with precedence rejected > consumed > expired > active. Treat rejected as a deliberate decline, distinct from a quote that was consumed by a transact.

Off-ramp (sell)

off_ramp is account-gated. It deny-closes with HTTP 501 unless explicitly enabled for the partner, pool, and environment. When enabled, off-ramp requires a crypto-receipt gate: the partner first sends the crypto leg to its dedicated per-partner deposit address from cryptoDepositAddress in /capabilities; 0Pools credits the partner-scoped crypto balance; only then can a sell trade debit that balance and move toward fiat payout. Do not retry a 501 in a loop — request enablement through account review.

Funding

GET /pools/funding returns your dedicated deposit instructions for the pre-funded fiat balance that backs on-ramp orders (EUR-only).

curl -X GET https://pools-api.0bit.app/v1/pools/funding \
  -H "Authorization: Bearer sk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Funding fieldUse it for
currencyThe funding currency (EUR).
ibanThe dedicated IBAN provided at runtime (may be null if not yet provisioned).
referenceThe reference to include with your deposit.
instructionsPartner-visible deposit instructions.

If the balance gate is off for the partner, this returns 404. If funding is enabled but not yet provisioned, iban is null with an operations note — wait for provisioning rather than retrying in a loop.

Status and reconciliation

Use read endpoints to join the quote to the resulting trade, balance, and ledger.

Read pathWhat to store
GET /pools/quotes/{quoteId}Quote status, expiry, rate, fee/spread fields, pair, amount, network.
GET /pools/transactions/{quoteId}Trade status by quote id. This poll advances settlement.
GET /pools/tradesTrade history with cursor pagination and filters (reconciliation).
GET /pools/trades/{transactId}Trade id, quote id, status, pair, amount, timestamps. Pure read; does not advance settlement.
GET /pools/{id}/balancePartner-visible balance and entitlement view.
GET /pools/ledgerPartner-scoped balance movements (credits/debits) for reconciliation.

GET /pools/trades uses cursor pagination: pass ?cursor=<last transactId>; the response is { data[], nextCursor, hasMore } with nextCursor null when exhausted. limit is 1–100 (default 50). Filter with side, status, created_after, and created_before.

The balance view returns { poolId, pair, available, tier, allowedPools, balance } with money as decimal strings. It deliberately does not echo access or KYC state (those surface only as 403 denial codes) and exposes no venue or external numbers.

Support and operations should be able to answer: which pool was selected, which quote was shown, whether it expired or was rejected, whether a trade was created, what status it reached, whether it later returned, and which request ids belong to the workflow.

HTTP status handling

Every response carries an X-Request-Id header (mirroring request_id in the unified error envelope { type, code, message, request_id, doc_url, statusCode }). Branch on statusCode, type, and code — never on the free-form message.

StatusMeaning for PoolsRecommended handling
400Validation problem (for example, missing destAddress on on_ramp).Fix the request or user input; do not retry unchanged.
401Missing or invalid authentication.Fix credentials before retrying.
402Insufficient balance to reserve at transact.Fund the balance (see /pools/funding), then retry.
404Object not found or belongs to another partner (cross-tenant).Check ids and ownership; never surface foreign ids.
409Quote consumed, expired, reservation failed, or idempotency-key/body conflict.Retrieve current state and branch; refresh the quote if stale.
429Per-partner 24h notional velocity cap exceeded at transact.Back off; the cap is a rolling window.
501off_ramp transact not enabled for public use.Do not retry; request enablement through account review.

403 uses stable denial codes (and 425 is not used)

Access failures return 403 with a stable code (pools_not_enabled, pool_access_suspended, kyc_not_approved, pool_not_allowed, key_mode_mismatch) — branch on code, not message. See the Errors page for the full denial table. Pools does not use 425.

Rate limits

Ordinary throttling is applied per client IP. Separately, an optional per-partner rolling 24-hour notional velocity cap can return 429 at transact when exceeded; idempotent retries are excluded so they are not double-counted. The velocity cap is off by default and configured per partner. Per-order minimum and maximum (minOrderUsdt / maxOrderUsdt) come from /capabilities.

Error and retry guidance

ConditionRecommended handling
Quote unavailableShow the unavailableReason and let the user choose another route; do not retry in a tight loop.
Quote expiredRequest a new quote and require the user to accept the new terms.
Duplicate transactReuse the existing trade; the same Idempotency-Key returns the existing trade.
Idempotency conflictA 409 means the key was reused with a different body; reconcile before issuing a new write.
Insufficient balanceFund the fiat balance from /pools/funding, then retry.
Off-ramp not enabledTreat 501 as terminal; request enablement through account review.
Foreign or missing quoteTreat as a hard 404; do not expose partner identifiers.
Network timeoutRetry transact server-side with the same idempotency key, then read current state.

Public documentation boundary

Public docs may describe:

  • Entitled pool discovery and capabilities.
  • Quote request (firm and indicative) and partner-visible quote fields.
  • Accept/reject behavior and derived quote status.
  • Trade status, the returned auto-refund, and partner-scoped reads.
  • Funding instructions, balance, and ledger for reconciliation.
  • Fee and spread fields only where approved.

Public docs must not describe:

  • Treasury balances or reserve allocation.
  • Liquidity-provider or settlement-provider identities and bank rails.
  • Internal routing or rebalancing logic and thresholds.
  • Provider contracts or market-maker terms.
  • Operational runbooks.
  • Risk controls beyond partner-facing status/error guidance.

On this page