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
- Node.js
- Python
- Go
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;
}
import os
import uuid
import requests
CBA_URL = os.environ["CBA_API_URL"]
TENANT_ID = os.environ["CBA_TENANT_ID"]
def cba_request(method, path, token, body=None):
headers = {
"Authorization": f"Bearer {token}",
"X-Tenant-ID": TENANT_ID,
}
if method != "GET" and body:
headers["Idempotency-Key"] = str(uuid.uuid4())
res = requests.request(method, f"{CBA_URL}{path}", headers=headers, json=body)
data = res.json()
if not res.ok:
raise CBAError(data["code"], data["message"], res.status_code)
return data
func cbaRequest(method, path, token string, body io.Reader) ([]byte, error) {
url := os.Getenv("CBA_API_URL") + path
req, err := http.NewRequest(method, url, body)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("X-Tenant-ID", os.Getenv("CBA_TENANT_ID"))
req.Header.Set("Content-Type", "application/json")
if method != http.MethodGet {
req.Header.Set("Idempotency-Key", uuid.NewString())
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return io.ReadAll(resp.Body)
}
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
- Webhooks — Receive real-time event notifications.
- Error Codes — Full error code reference.
- Sandbox Testing — Test your integration safely.