Skip to content

Quickstart — Node.js / TypeScript

Connect your first bank account in sandbox mode with @budgetbakers/partner-sdk. You need a partner account in the partner portal — registration provisions sandbox access instantly, no card required.

1. Install

sh
npm install @budgetbakers/partner-sdk

Node ≥ 20; zero runtime dependencies; ESM and CJS. Until the npm package is published, install the tarball from the downloads page instead: npm install ./budgetbakers-partner-sdk-0.1.0.tgz.

2. Create the client

Your API key selects the mode: bb_test_… keys run sandbox (fictional *_xf banks), bb_live_… keys run live. Keys are server-side only — never embed them in client-side code or URLs.

ts
import { BudgetBakers } from '@budgetbakers/partner-sdk';

const bb = new BudgetBakers({ apiKey: '{{BB_API_KEY}}' });

const config = await bb.partner.getConfig();
console.log(config.mode); // "sandbox"

3. Create an end-user client

A client is one of your end users — bank connections, accounts and transactions all belong to a client, and the X-Client-Id scoping keeps each user's data isolated. Pass your own user id as externalId and the call becomes an upsert — safe to call on every login, no duplicates, no need to store a BudgetBakers-id mapping on your side:

ts
const client = await bb.clients.create({ externalId: 'user-42' });
const scope = bb.client(client.id); // all user-scoped calls go through this

4. Start the hosted connect flow

Create a connect session and send the user to hostedUrl (web redirect, or the mobile Link SDKs — see the iOS/Android quickstarts).

The returnUrl works like an OAuth redirect URI: the portal (Apps → return URLs) holds your allowlist of patterns — one per platform (web URL, iOS universal link, Android app link, custom scheme) — and each session passes the concrete URL for where this user should land. It must match a registered pattern, otherwise the create fails with validation_error.

ts
const session = await scope.connectSessions.create({
  returnUrl: 'https://app.example.com/bb-callback',
});
redirectUserTo(session.hostedUrl);

The user picks a bank (sandbox: e.g. Sandbox Bank XF), authenticates, and is redirected back to your returnUrl. The redirect is a UX signal — the authoritative result is the session state:

ts
const done = await scope.connectSessions.waitForTerminal(session.sessionId);
if (done.state === 'Completed') {
  console.log('connected:', done.connectionId);
}

5. Receive webhooks

Configure your webhook URL in the portal (per mode); every delivery is signed. See the webhook verification guide for the full contract.

ts
app.post('/webhooks/bb', express.raw({ type: '*/*' }), (req, res) => {
  const result = bb.webhooks.verify(
    ['{{BB_WEBHOOK_SECRET}}'],           // ALL active secrets (two during rotation)
    req.header('X-BB-Signature') ?? '',
    req.body,                            // the RAW body bytes
  );
  if (result !== 'valid') return res.status(401).end();

  const event = bb.webhooks.parseEvent(req.body);
  if (event.kind === 'event' && event.type === 'ConnectionCreateSuccess') {
    // fetch data, update your records …
  }
  res.status(200).end(); // always 2xx for verified deliveries — unknown types included
});

6. Read accounts and transactions

ts
const accounts = await scope.connections.listAccounts(connectionId);

for await (const tx of scope.accounts.transactions(accounts[0].id)) {
  // tx.amount is a DecimalString ("1234.56") — money is never a float.
  // Only `id` is guaranteed non-null; treat every other field as nullable.
}

Error handling

Branch on error.code only — never parse messages (full code reference):

ts
import { PartnerApiError } from '@budgetbakers/partner-sdk';

try {
  await scope.connections.refresh(connectionId);
} catch (err) {
  if (err instanceof PartnerApiError && err.code === 'refresh_cooldown') {
    console.log('retry after', err.nextRefreshPossibleAt);
  } else {
    throw err;
  }
}

The SDK already retries 429/5xx with exponential backoff (honoring Retry-After) and auto-generates Idempotency-Key on creates.

Going live

Complete the go-live checklist in the portal (card verification, business check, terms) — production unlocks automatically and you can mint a bb_live_… key. The code above works unchanged; only the key differs.