API Conventions
Versioning
Path-based: /v1/.... No header-based versioning — keeps client debugging trivial (Stripe-style,
not GitHub-style). A breaking change ships as /v2/... alongside a maintained /v1/... for a
documented deprecation window (minimum 12 months once TBBN has production merchants).
Auth headers
- Merchant API keys:
Authorization: Bearer sk_live_...(production) orAuthorization: Bearer sk_sandbox_...(sandbox). Prefix determines environment — never inferred from the host. - Seller sessions (TBBN Exchange / merchant-embedded widgets acting on behalf of a federated
seller):
Authorization: Bearer <seller_jwt>, issued byauth-service's seller OTP/magic-link flow. - Internal service-to-service calls: a short-lived signed internal-principal header issued once
by
api-gatewayafter it validates the inbound credential — internal services never re-validate merchant API keys themselves.
Idempotency
Every mutating endpoint (POST, PATCH, DELETE) on a merchant-facing route requires an
Idempotency-Key header. This is not optional — checkout orchestration and reservation calls
are the first things a merchant integration retries on timeout, and a duplicate reservation or
duplicate checkout-orchestration call must be a no-op, not a duplicate side effect. Keys are
scoped per-merchant per-endpoint, retained 24h.
Pagination
Cursor-based on every list endpoint: GET /v1/resource?cursor=<opaque>&limit=<n, default 25, max 100>. Response includes { data: [...], nextCursor: string | null }. No offset/page-number
pagination anywhere — it doesn't hold up under concurrent writes to a live listing/offer feed.
Error envelope
{
"error": {
"code": "LISTING_NOT_FOUND",
"message": "No listing found with id lst_01hxyz...",
"requestId": "req_01hxyz...",
"details": {}
}
}
code is a stable, documented machine-readable string (SCREAMING_SNAKE_CASE); message is
human-readable and may change; requestId is always present and is what merchants should quote
in support requests. HTTP status codes follow standard REST semantics (400 validation, 401/403
auth, 404 not found, 409 conflict/idempotency mismatch, 422 business-rule violation e.g.
attempting to set both requestedAmount and willingToPay, 429 rate limit, 5xx TBBN-side
fault).
Rate limiting
X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset headers on every response.
Limits are per-API-key, tiered per docs/business/pricing.md's plan (sandbox keys are
rate-limited but not billed for overage; production keys hit their plan's included-usage rate
before overage billing kicks in — rate limiting and usage metering are related but distinct
mechanisms, see usage_events in db-erd.md).
Webhook signatures
See docs/architecture/event-webhook-catalog.md — X-TBBN-Signature: t=<ts>,v1=<hmac>,
5-minute replay window, verification helper shipped in every SDK.
Localization / globalization
Every monetary field is {amount: decimal, currency: ISO 4217 code} — never a bare number.
Phone numbers are always E.164 on the wire. Dates/times are always timestamptz /ISO 8601 with
explicit offset, never naive local time. Accept-Language is honored on any endpoint that
returns human-readable text (error messages, category/taxonomy labels).