0Bit Documentation

0Pools quickstart

Build an approved 0Pools server-side flow from entitlement discovery to quote, transact, status, webhook, and reconciliation.

This quickstart is the fastest safe path through 0Pools. It shows the developer flow without pretending 0Pools is self-serve. Before this works in production, the partner must be active, KYC/KYB approved, tiered, allowlisted for pools, and provisioned for funding/webhooks where required.

Implementation flow and API details

Start with the flow below to understand the full 0Pools lifecycle. Use the 0Pools API reference for endpoint schemas, request fields, response objects, status codes, and examples.

End-to-end picture

1. Confirm access

No money route works until every gate is open.

GateWhat must be trueFailure shape
Global product0Pools backend enabled for the environment.Product disabled.
Partner keyCorrect sk_* or pk_* for test/live.401 or mode mismatch.
Partner accessPartnerPoolAccess row exists and is active.403.
VerificationKYC/KYB status is approved.403.
TierPricing tier is assigned.403 / access suspended.
Allowed poolsRequested pool id is in allowedPools.403 or cross-tenant 404.
FundingPrefunded balance exists where required.402 insufficient balance.

Before building the flow, confirm which pools, sides, networks, limits, and funding model are approved for your account.

2. Discover entitled pools

Use discovery as the source of truth for what to display.

curl https://pools-api.0bit.app/v1/pools \
  -H "Authorization: Bearer $OBIT_PUBLISHABLE_OR_SECRET_KEY"

Example response:

{
  "pools": [
    {
      "id": "EUR-USDT",
      "pair": "EUR/USDT",
      "fiatCurrency": "EUR",
      "cryptoCurrency": "USDT",
      "available": true
    }
  ]
}

Do not hard-code pool ids from examples. Entitlements can differ by partner, market, and environment.

3. Read capabilities

Capabilities tell your UI what it can safely show: sides, networks, fees, limits, and optional deposit instructions.

CapabilityUse it to
supportedNetworksBuild network selectors.
sidesHide disabled buy/sell paths.
minOrderUsdt / maxOrderUsdtValidate amount before quote.
drawFeeBps / spreadCapBpsExplain partner-visible economics.
cryptoDepositAddressShow off-ramp deposit details only when enabled.

4. Preview with an indicative quote

Indicative quotes let you refresh a price while the user edits. They are not persisted and cannot be executed.

curl -X POST https://pools-api.0bit.app/v1/pools/EUR-USDT/quote \
  -H "Authorization: Bearer $OBIT_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "side": "on_ramp",
    "fiatCurrency": "EUR",
    "cryptoCurrency": "USDT",
    "amount": "100.00",
    "type": "indicative"
  }'

5. Lock a firm quote

When the user is ready, request a firm quote. A firm quote is short-lived, single-use, and returns quoteId.

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

For a buy, the destination wallet is locked here. Execution cannot redirect delivery later.

6. Execute once

Execute with an Idempotency-Key. Treat a successful response as reserved, not final settlement.

curl -X POST https://pools-api.0bit.app/v1/pools/EUR-USDT/transact \
  -H "Authorization: Bearer $OBIT_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order_123_accept_1" \
  -d '{ "quoteId": "pq_test_123" }'

Example acknowledgement:

{
  "transactId": "txn_test_456",
  "status": "reserved",
  "quoteId": "pq_test_123",
  "idempotent": false
}

7. Track status

Poll by quote id or consume terminal webhooks.

curl https://pools-api.0bit.app/v1/pools/transactions/pq_test_123 \
  -H "Authorization: Bearer $OBIT_SECRET_KEY"
StatusAction
reservedKeep showing pending. Do not credit final balance.
settledTerminal success. Update your ledger.
failedTerminal failure. Follow your refund/support policy.
releasedClean unwind. Let the user retry with a fresh quote.
returnedDownstream return after settlement. Reconcile and notify support.

8. Reconcile

Your production record should be able to answer: who requested the flow, what pool was quoted, what terms were accepted, which trade was created, what terminal state arrived, and which ledger movement resulted.

{
  "order_id": "order_123",
  "environment": "sandbox",
  "pool_id": "EUR-USDT",
  "quote_id": "pq_test_123",
  "transact_id": "txn_test_456",
  "side": "on_ramp",
  "amount": "100.00",
  "status": "reserved",
  "idempotency_key": "order_123_accept_1",
  "request_id": "req_..."
}

Before and after

Before 0PoolsWith 0Pools
Partner builds ad hoc quote logic.Partner calls one quote lifecycle.
User accepts a price with unclear expiry.Firm quote has explicit expiry.
Duplicate clicks can double-submit.Execution is idempotent.
Support searches across unrelated systems.Quote, trade, request, and event ids join the path.
Ledger updates rely on UI state.Ledger updates from server status/webhook state.

Production integration artifacts

A production quickstart should leave the partner backend with one durable lifecycle record. Do not split discovery, quote, execution, status, and reconciliation across disconnected tables.

ArtifactRequired contentWhy it matters
Server order rowpartner_order_id, user reference, side, amount, asset, network, destination reference.Lets product, support, and finance speak about the same request.
0Pools quote rowpool_id, quote_id, quote type, expiry, source amount, target amount, fee/spread fields returned to the partner.Proves exactly what the user accepted.
Execution rowtransact_id, idempotency_key, execution attempt timestamp, current status.Prevents duplicate accepts and makes retries explainable.
Event rowwebhook/event id, signature verification result, received timestamp, processed timestamp.Proves async delivery and replay handling.
Ledger rowdebit/credit amount, currency, ledger reference, terminal status.Gives finance a closeable record.

Minimal server flow

async function acceptPoolQuote(order: PartnerOrder) {
  const idempotencyKey = `${order.id}:accept:${order.acceptVersion}`;

  const quote = await pools.createFirmQuote({
    poolId: order.poolId,
    side: order.side,
    sourceAmount: order.sourceAmount,
    destination: order.destinationRef,
  });

  await db.poolQuotes.insert({
    orderId: order.id,
    quoteId: quote.quoteId,
    expiresAt: quote.expiresAt,
    acceptedTerms: quote,
  });

  const trade = await pools.transact({ quoteId: quote.quoteId }, { idempotencyKey });

  await db.poolTrades.upsert({
    orderId: order.id,
    quoteId: quote.quoteId,
    transactId: trade.transactId,
    status: trade.status,
    idempotencyKey,
  });

  return trade;
}

Acceptance tests before launch

TestExpected result
Execute the same accepted quote twice with the same idempotency key.One trade record is returned; no duplicate user debit.
Execute after expiresAt.The backend rejects or requests a fresh quote; the UI never silently refreshes accepted terms.
Drop the webhook once and poll status.The ledger still reaches the correct terminal state.
Send a malformed destination/network combination.The error is mapped to user-safe copy and support keeps the request id.

Implementation depth

This page is about launching the first server-side 0Pools flow. For developers, the durable boundary is simple: your product should depend on entitled pools, capabilities, quote ids, trade ids, statuses, and ledger joins; it should not depend on route choice, venue balances, reserve allocation, and treasury movement. That boundary is what lets 0Pools improve liquidity operations without forcing every partner to rebuild product code.

Product scenario

A neobank lets a user buy USDT from EUR inside its own app. The app previews an indicative quote while the user edits the amount, locks a firm quote only on confirmation, executes once from the server, and updates the ledger only from terminal status.

Before and after in practice

Before 0PoolsWith 0Pools
The launch checklist lives across Slack, spreadsheets, and partial vendor dashboards. A duplicate click or expired price can create an investigation before anyone has a common id chain.The partner stores pool id, quote id, transact id, status, request id, and ledger row from the same lifecycle, so support and finance can reconstruct the order from one record chain.
Product teams infer liquidity behavior from provider-specific screens or manual notes.Product teams branch on 0Pools objects: pool, quote, trade, balance, webhook, and ledger.
Support asks engineering to reconstruct state from logs.Support starts from request id, quote id, trade id, event id, and ledger id.

System flow

Records to keep

RecordWhy it mattersExample
partner_order_idConnects your product order to 0Pools state.order_7841
quote_idExplains exactly which price was accepted.pq_test_123
transact_idConnects execution, webhooks, and reconciliation.pt_test_456
idempotency_keyProtects the accept button and server retries.order_7841_accept_1

A useful implementation stores these records before adding optional analytics. Analytics can be rebuilt from durable state; missing ids usually cannot be reconstructed after a customer-impacting failure.

Example product record

{
  "product_area": "quickstart",
  "partner_reference": "partner_order_123",
  "pool_id": "EUR-USDT",
  "quote_id": "pq_test_123",
  "transact_id": "pt_test_456",
  "status": "reserved",
  "request_id": "req_01HZY0POOLS",
  "reconciliation_state": "open"
}

This record is intentionally partner-facing. It references 0Pools objects and your own product ids, but it does not include private liquidity routes, treasury balances, raw provider payloads, or customer secrets.

Failure modes and recovery

Failure modeProduct response
Expired firm quoteCreate a fresh quote and ask the user to confirm again.
Duplicate execute requestReuse the same idempotency key and render the returned trade.
Unavailable poolHide or disable the lane until discovery/capabilities show it as available.
Reserved too longKeep the user pending and reconcile from status/webhook before final ledger credit.

Use-case patterns

PatternHow 0Pools helps
Wallet top-upUser buys stablecoin for a self-custody wallet.
Neobank crypto tabBanking app exposes crypto buy flow without becoming a liquidity desk.
Card program fundingA card product funds a crypto balance with bounded quote acceptance.

Developer checklist

  • Read runtime discovery and capabilities before rendering enabled actions.
  • Create firm quotes only when the user is ready to accept the terms.
  • Execute from your server with an idempotency key.
  • Treat webhooks and status reads as the source of truth for settlement state.
  • Reconcile by durable ids first, then by amount and timestamp.
  • Do not branch product behavior on provider names, route names, reserve balances, or treasury assumptions.

Next pages

On this page