0Bit Documentation

0Pools API

Approved partner 0Pools discovery, quote, transact, status, trade, funding, ledger, and balance operation groups.

0Pools is the approved-partner API for headless pool liquidity workflows. It lets an entitled partner discover the pools available to its account, request firm or indicative quotes, execute exactly once against a firm quote, poll or read trade state, and reconcile balance movements.

This reference is OpenAPI-backed and public-safe. It explains the contract partners can build against without exposing liquidity sources, routing internals, reserve allocation, treasury balances, provider names, market-maker terms, or runbooks.

OpenAPI-backed, gated partner contract

The machine-readable OpenAPI 3.1 contract for 0Pools is available at /openapi/pools-v1.yaml. Use it for client generation and schema validation. Use these pages for lifecycle rules, public boundaries, examples, and production decisions.

Contract snapshot

ItemCurrent shape
OpenAPI title0Pools Partner API
OpenAPI version2026-05-25
Published operations12 partner-facing operations
Main production hosthttps://pools-api.0bit.app/v1
Main sandbox hosthttps://pools-api-sandbox.0bit.app/v1
Discovery credentialPublishable key for GET /pools only
Money-moving credentialSecret key from your server
AvailabilityGated early access, provisioned per partner

The API is not self-serve. A valid key does not mean every pool, side, rail, balance gate, or environment is enabled. Your account entitlement decides which pools appear and which operations succeed.

What this API covers

  • Entitled pool discovery and current partner-visible availability.
  • Per-pool capabilities: sides, networks, limits, fees, spreads, tier, and balance gates.
  • Firm quotes for executable short-lived locks.
  • Indicative quotes for price-only previews.
  • One-time idempotent execution against firm quotes.
  • Status polling that can advance settlement.
  • Pure trade reads for display, support, and reconciliation.
  • Partner-visible balance and funding views.
  • Partner-scoped ledger entries for balance reconciliation.

Public boundary

This reference covers partner-visible discovery, quote, transact, status, trade, funding, ledger, and balance behavior. It does not expose liquidity sources, provider names, route priority, market-maker terms, reserve levels, treasury account balances, top-up runbooks, or settlement worker internals.

Authentication map

CredentialWhere it belongsAllowed Pools useNot allowed
pk_test_* / pk_live_*Browser/mobile where approvedGET /pools discovery onlyQuotes, transacts, balance, funding, ledger, trade reads
sk_test_* / sk_live_*Your server onlyCapabilities, quote, transact, status, trades, balance, funding, ledgerBrowser/mobile client code
Idempotency-KeyYour server-generated write headerRequired for POST /pools/{id}/transactReusing across different quote executions
X-Request-IdResponse headerSupport correlationBusiness logic branching

Discovery is not execution approval

GET /pools can show an entitled pool, but quote and transact calls still enforce account status, KYC/KYB state, tier, balance, side, route availability, and environment gates.

Operation map

AreaOperationMethod and pathAuth boundaryUse it for
DiscoverylistPoolsGET /poolsPublishable keyList pools visible to your account.
DiscoverygetPoolCapabilitiesGET /pools/{id}/capabilitiesSecret key + entitlementRead supported sides, networks, order bounds, fees, spread caps, tier, and balance gates.
QuotescreateQuotePOST /pools/{id}/quoteSecret key + entitlementRequest a firm executable quote or indicative price.
TransactionstransactPOST /pools/{id}/transactSecret key + idempotency + entitlementExecute exactly once against a firm quote.
TransactionsgetTransactionStatusGET /pools/transactions/{quoteId}Secret keyPoll by quote id; this poll can advance settlement.
ReconciliationlistTradesGET /pools/tradesSecret keyCursor-paginated trade history for support and reconciliation.
ReconciliationgetTradeGET /pools/trades/{transactId}Secret keyPure read of one trade; does not advance settlement.
QuotesgetQuoteGET /pools/quotes/{quoteId}Secret keyRetrieve quote state without consuming it.
QuotesrejectQuotePOST /pools/quotes/{quoteId}/rejectSecret keyDecline an active quote so it cannot be executed.
DiscoverygetPoolBalanceGET /pools/{id}/balanceSecret key + entitlementRead partner-visible balance and entitlement context.
ReconciliationgetFundingInstructionsGET /pools/fundingSecret keyRead dedicated funding instructions where enabled.
ReconciliationlistLedgerEntriesGET /pools/ledgerSecret keyReconcile balance credits and debits.

End-to-end lifecycle

The important split is between driver reads and display reads. GET /pools/transactions/{quoteId} can progress a reserved trade. GET /pools/trades and GET /pools/trades/{transactId} are pure reads for dashboards, support, and reconciliation.

Quote outcomes

OutcomeHTTP statusFieldsWhat your app should do
Firm executable quote200available: true, executable: true, quoteId, expiresAtShow a short accept window and execute once before expiry.
Indicative quote200available: true, executable: false, no quoteIdShow price-only preview; never pass it to transact.
Fail-soft unavailable200available: false, unavailableReason, no quoteIdShow unavailable state, try another pool, or back off.
Validation failure400Error envelopeFix request body or user input.
Entitlement denied403Error envelope with denial codeConfirm account access; do not retry in a loop.
Unknown scoped object404Error envelopeTreat as not found for this partner/environment.

Fail-soft 200 responses are intentional. Branch on available and executable, not just HTTP status.

Example: quote then execute

curl -X POST https://pools-api-sandbox.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",
    "cryptoNetwork": "tron",
    "type": "firm",
    "destAddress": "0x52908400098527886E0F7030069857D2E4169EE7",
    "destNetwork": "arbitrum"
  }'

curl -X POST https://pools-api-sandbox.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-000000000456" \
  -d '{ "quoteId": "pq_test_8f3c000000000123" }'
const quoteResponse = await fetch('https://pools-api-sandbox.0bit.app/v1/pools/EUR-USDT/quote', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.OBIT_POOLS_SECRET_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    side: 'on_ramp',
    fiatCurrency: 'EUR',
    cryptoCurrency: 'USDT',
    amount: '100.00',
    cryptoNetwork: 'tron',
    type: 'firm',
    destAddress: '0x52908400098527886E0F7030069857D2E4169EE7',
    destNetwork: 'arbitrum',
  }),
});

const quote = await quoteResponse.json();
if (!quote.available || !quote.executable) {
  throw new Error(`Pool unavailable: ${quote.unavailableReason ?? 'not executable'}`);
}

const tradeResponse = await fetch('https://pools-api-sandbox.0bit.app/v1/pools/EUR-USDT/transact', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.OBIT_POOLS_SECRET_KEY}`,
    'Content-Type': 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
  body: JSON.stringify({ quoteId: quote.quoteId }),
});

const trade = await tradeResponse.json();
import os
import uuid
import requests

quote = requests.post(
    "https://pools-api-sandbox.0bit.app/v1/pools/EUR-USDT/quote",
    headers={
        "Authorization": f"Bearer {os.environ['OBIT_POOLS_SECRET_KEY']}",
        "Content-Type": "application/json",
    },
    json={
        "side": "on_ramp",
        "fiatCurrency": "EUR",
        "cryptoCurrency": "USDT",
        "amount": "100.00",
        "cryptoNetwork": "tron",
        "type": "firm",
        "destAddress": "0x52908400098527886E0F7030069857D2E4169EE7",
        "destNetwork": "arbitrum",
    },
).json()

if not quote.get("available") or not quote.get("executable"):
    raise RuntimeError(f"Pool unavailable: {quote.get('unavailableReason')}")

trade = requests.post(
    "https://pools-api-sandbox.0bit.app/v1/pools/EUR-USDT/transact",
    headers={
        "Authorization": f"Bearer {os.environ['OBIT_POOLS_SECRET_KEY']}",
        "Content-Type": "application/json",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={"quoteId": quote["quoteId"]},
).json()

Object model

On-ramp and off-ramp behavior

SideMeaningAmount rolePublic status
on_rampBuy crypto with fiatamount is the fiat amount paid inSupported flow for approved partners.
off_rampSell crypto for fiatamount is the crypto amount soldAccount-gated and not publicly enabled by default.

For on-ramp delivery, the destination address is locked on the quote and copied to the trade. Do not collect a new destination address at transact time. For off-ramp, build only after your account has explicit enablement and the crypto-receipt path is confirmed.

Status model

StatusMeaningDeveloper rule
quotedQuote exists but has not been executedExecute before expiry or reject it.
reservedTrade is accepted and waiting for settlement progressPoll GET /pools/transactions/{quoteId}.
settledTrade reached successful settlement stateReconcile trade and ledger.
releasedReserved funds or lock were releasedStop polling and reconcile.
failedTrade failed terminallyShow recoverable support path and reconcile.
returnedA previously settled value was reversed and credited backReconcile the ledger credit; webhooks alone are not enough.

The returned state is why a webhook-only integration is incomplete. Continue polling after settlement where your flow requires reversal detection, or reconcile the ledger on a schedule.

Reconciliation model

ReadAdvances settlement?Use it for
GET /pools/transactions/{quoteId}YesMove a reserved trade toward terminal state and catch returned state.
GET /pools/tradesNoBack-office history, filters, export, support.
GET /pools/trades/{transactId}NoOne trade display or support lookup.
GET /pools/{id}/balanceNoBalance and entitlement context.
GET /pools/ledgerNoCredits, debits, refunds, and balance reconciliation.

Store your own order id, pool id, quote id, trade id, idempotency key, request id, status, amount/rate strings, fee/spread bps, and timestamps. Those records let support investigate without any private route details.

Error and retry model

OutcomeWhat to do
200 with available: falseDo not transact. Show unavailable state or try another entitled pool.
400 validation errorFix request body, address, network, side, or amount.
401 auth errorCheck key and environment.
402 insufficient balanceFund the balance where enabled, then retry from a fresh quote if needed.
403 entitlement or key-mode errorConfirm account access, KYC/KYB state, side enablement, pool entitlement, and key type.
404 scoped object missingTreat as not found for this partner/environment.
409 quote conflictRetrieve quote/trade state; do not blindly retry.
429 rate limitBack off with jitter.
5xx or timeoutRetry with bounded backoff; writes must use idempotency.
501 off-ramp not enabledStop the flow and request enablement.

Production checklist

  • Account has explicit 0Pools approval.
  • Pool appears in GET /pools for the active environment.
  • Your server reads capabilities before quoting.
  • Browser code never uses sk_*.
  • Amounts and rates are strings, never floats.
  • Firm quote expiry is enforced in your UI.
  • Transact uses a durable idempotency key.
  • Status polling uses quoteId and recognizes terminal states.
  • Trade reads are used for display and reconciliation.
  • Ledger reconciliation runs after settlement and on a schedule.
  • Support tooling can search by your order id, quote id, trade id, and request id.
  • The UI never exposes source/provider/route internals.

On this page