Skip to main content

Server Integration

This guide covers how to integrate the CBA API from your backend — making authenticated requests, handling idempotency, retrying failures, and processing webhook events.

Architecture

┌────────────┐     ┌────────────────┐     ┌───────────────────┐
│ Your Client│────▶│ Your Backend │────▶│ CBA API Gateway │
│ (mobile/ │ │ (proxy layer) │ │ api.korastratum │
│ web app) │ │ │ │ .com/cba │
└────────────┘ └────────────────┘ └───────────────────┘

API Client

const CBA_URL = process.env.CBA_API_URL; // https://api.korastratum.com/api/v1/cba
const TENANT_ID = process.env.CBA_TENANT_ID;

async function cbaRequest(method, path, token, body = null) {
const headers = {
Authorization: `Bearer ${token}`,
"X-Tenant-ID": TENANT_ID,
"Content-Type": "application/json",
};

// Add idempotency key for writes
if (method !== "GET" && body) {
headers["Idempotency-Key"] = crypto.randomUUID();
}

const res = await fetch(`${CBA_URL}${path}`, {
method,
headers,
body: body ? JSON.stringify(body) : undefined,
});

const data = await res.json();

if (!res.ok) {
throw new CBAError(data.code, data.message, res.status, data.request_id);
}

return data;
}

Error Handling

Map CBA API errors to your own HTTP responses:

class CBAError extends Error {
constructor(code, message, status, requestId) {
super(message);
this.code = code;
this.status = status;
this.requestId = requestId;
}
}

// Express error handler
app.use((err, req, res, next) => {
if (err instanceof CBAError) {
return res.status(err.status).json({
error: err.message,
code: err.code,
request_id: err.requestId,
});
}
res.status(500).json({ error: "Internal server error" });
});

Retry Strategy

Retry on transient errors (503 Service Unavailable, network timeouts) with exponential backoff. The Idempotency-Key ensures retries are safe for write operations.

async function withRetry(fn, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (err) {
if (err.status === 503 && i < maxRetries - 1) {
await new Promise((r) => setTimeout(r, 1000 * Math.pow(2, i)));
continue;
}
throw err;
}
}
}
warning

Never retry 4xx errors — they indicate a problem with the request that must be fixed before retrying. Only retry 503 and network-level failures.

Idempotency Best Practices

  • Generate a unique key per logical operation, not per HTTP request. If you retry, reuse the same key.
  • Store the key alongside your internal transaction record so retries after crashes use the same key.
  • Use UUIDs or a combination of {entity_type}-{entity_id}-{action}-{timestamp} for deterministic keys.
// Good: deterministic key tied to the business operation
const idempotencyKey = `transfer-${invoiceId}-${Date.now()}`;

// Bad: random key per retry (defeats idempotency)
const idempotencyKey = crypto.randomUUID(); // regenerated on retry

Cursor Pagination

When fetching paginated lists, iterate through all pages:

async function fetchAllAccounts(token) {
const accounts = [];
let cursor = null;

do {
const params = new URLSearchParams({ limit: "100" });
if (cursor) params.set("cursor", cursor);

const data = await cbaRequest("GET", `/api/v1/accounts?${params}`, token);
accounts.push(...data.accounts);
cursor = data.has_more ? data.next_cursor : null;
} while (cursor);

return accounts;
}

Next Steps