0Bit Documentation

Your First Integration

Build your first 0Bit integration by choosing a product track, creating the first product object, and reconciling trusted state.

This walkthrough is for all 0Bit products. It starts with the product decision, then shows the shared implementation pattern. The detailed code sample uses 0Gate as the concrete example because it exercises the most visible server-to-browser-to-webhook loop, but 0Pools, 0Base, and 0Link follow the same backend ownership and reconciliation rules with product-specific objects.

Pick the integration track

TrackBuild firstContinue in product docs
0GateServer-created session, hosted/embed flow, signed event handler.0Gate quickstart
0PoolsEntitled pool discovery, quote request, quote execution, trade-status reconciliation.0Pools quickstart
0BaseCheckout/payment intent/payment link/invoice object plus settlement/report status.0Base quickstart
0LinkRoute/workflow review record plus route confirmation and fallback-state handling.0Link quickstart
0Pools ScanActivity/status read and support-facing investigation view.0Pools Scan

End-to-end flow

1. Model your internal record

Create your own record before calling 0Bit:

type CheckoutRecord = {
  id: string;
  userId: string;
  amount: string;
  currency: string;
  status: 'draft' | 'session_created' | 'pending' | 'completed' | 'failed' | 'cancelled';
  obitProduct: '0gate' | '0pools' | '0base' | '0link' | 'scan';
  obitObjectId?: string;
  obitEventIds: string[];
  idempotencyKey: string;
};

This record is the anchor for support and finance. Do not make the 0Bit session id your only record id.

2. Create the first product object from your backend

The object depends on the track:

ProductFirst backend call
0GateCreate a Gate session.
0PoolsList entitled pools, then create or lock a quote.
0BaseCreate a checkout, payment intent, payment link, or invoice.
0LinkCreate or review route/workflow context where enabled.
0Pools ScanRead activity/status records by id or filter.

Example 0Gate track:

import { GateClient } from '@0bit/gate';

const gate = new GateClient({
  apiKey: process.env.OBIT_SECRET_KEY!,
  baseUrl: 'https://gate-api-sandbox.0bit.app',
});

export async function createCheckoutSession(record: CheckoutRecord) {
  const session = await gate.sessions.create(
    {
      amount: record.amount,
      currency: record.currency,
      return_url: 'https://app.partner.example/checkout/done',
      metadata: {
        checkout_id: record.id,
        user_id: record.userId,
      },
    },
    {
      idempotencyKey: record.idempotencyKey,
    },
  );

  await saveCheckout({
    ...record,
    status: 'session_created',
    obitObjectId: session.id,
  });

  return {
    checkoutId: record.id,
    clientSecret: session.client_secret,
  };
}

If your SDK version does not support an options object for idempotency, send the same call through REST and include the Idempotency-Key header.

3. Run the product UX or server flow

0Gate usually has a browser-visible hosted/embed flow. 0Pools is usually headless and server-first. 0Base can be hosted checkout, payment link, or object-first. 0Link is route/workflow review. Match the client behavior to the product contract.

Example 0Gate browser flow:

import { RampCheckout } from '@0bit/gate/react';

export function CheckoutPage({ clientSecret }: { clientSecret: string }) {
  return (
    <RampCheckout
      publishableKey={process.env.NEXT_PUBLIC_OBIT_PUBLISHABLE_KEY!}
      clientSecret={clientSecret}
      onSuccess={({ sessionId }) => {
        console.log('Show confirmation UI while backend verifies', sessionId);
      }}
      onError={({ code, message }) => {
        console.warn(code, message);
      }}
    />
  );
}

The browser can show progress. Your backend decides final state after a verified event or trusted status read.

4. Receive and verify the webhook or trusted status

import crypto from 'node:crypto';
import express from 'express';

const app = express();

app.post('/webhooks/0bit', express.raw({ type: 'application/json' }), async (req, res) => {
  const rawBody = req.body.toString('utf8');
  const signature = req.header('Gate-Signature') ?? '';

  if (!verifyGateSignature(rawBody, signature, process.env.OBIT_WEBHOOK_SECRET!)) {
    return res.sendStatus(401);
  }

  const event = JSON.parse(rawBody);

  if (await eventAlreadyProcessed(event.id)) {
    return res.sendStatus(200);
  }

  await storeEvent(event.id, event);
  await applyCheckoutEvent(event);

  return res.sendStatus(200);
});

function verifyGateSignature(rawBody: string, header: string, secret: string) {
  const parts = Object.fromEntries(header.split(',').map((part) => part.split('=')));
  const timestamp = Number(parts.t);
  const expected = crypto.createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest('hex');
  return crypto.timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(parts.v1, 'hex'));
}

5. Apply business state

Keep the state transition narrow:

Event type or statusBackend action
Completed/succeeded terminal stateMark your record complete and fulfill once.
CancelledMark the record cancelled and let the user restart.
FailedMark failed, store reason/code, and show a recoverable path where appropriate.
Pending/processingKeep your record pending and wait for the next trusted state.
Unknown event typeStore it, return 2xx if verified, and alert for review without breaking delivery.

Do not run fulfillment twice. Store processed event ids and also guard by your own record status.

6. Reconcile

Your support and finance view should be able to answer:

QuestionRecord field
Which user/order started this?Your record id and user id.
Which 0Bit object did it create?obitSessionId or product-specific object id.
Which request created it?Idempotency key and request timestamp.
Which event closed it?Webhook event id and event timestamp.
What did the user see?Browser callback logs and return URL outcome.
What does 0Bit show?Trusted API read, report/ledger row, or Scan activity where applicable.

Production checklist

Credentials separatedTest and live values are not shared, and secret values never reach the browser.
Origins configuredBrowser origins and return URLs are registered for the environment you are testing.
Writes are idempotentEvery state-changing write has a stored idempotency key for safe retry behavior.
Webhooks are verifiedRaw-body signature verification, duplicate event handling, and event storage are tested.
Operations can reconcileRequest ids, event ids, object ids, and your own references are logged together.
Launch wording approvedProduct, Compliance, Operations, and Security approve live region and asset wording.
CheckPass condition
Sandbox flowSession create, hosted flow, callback, webhook, and reconciliation all work in test mode.
Webhook endpointUses raw body, verifies Gate-Signature, dedupes event ids, and returns 2xx on duplicates.
Secret handlingNo sk_* or whsec_* in browser bundle, logs, analytics, or screenshots.
Allowed domainsLocal, staging, and production origins are configured before each environment test.
Live switchProduction uses live host, sk_live_*, pk_live_*, and live webhook secret together.

Where to go next

On this page