TBBN.Merchant Platform docs
APIdocs/api/api-conventions.md

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) or Authorization: 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 by auth-service's seller OTP/magic-link flow.
  • Internal service-to-service calls: a short-lived signed internal-principal header issued once by api-gateway after 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.mdX-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).