Go-Live Checklist

The pre-production checklist — credentials, ordering correctness, secrets, webhooks, treasury, and resilience.

Run this list before pointing real traffic at your integration. Every item links back to the page that explains it.

Credentials and access

  • API keys live in a secret manager; nothing in source control, browsers, or mobile apps. (Authentication)
  • Scopes are split by system: display keys can't order, ordering keys can't withdraw, wallet.withdraw exists only in treasury tooling.
  • Keys that hand codes to customers hold orders.reveal (or are the purchasing key with orders.create); support and reporting tools hold orders.read only and never see codes. (Fulfillment)
  • Each deployment checks its key with GET /api/v1/me at startup — scopes and expiry as expected.
  • Production keys carry a CIDR allow-list pinned to your egress addresses, and 403 ip_not_allowed alerts operations.
  • Key rotation is rehearsed: issue → deploy → revoke, with both valid during the overlap.
  • 401/403 responses page someone — they mean revoked/expired/mis-scoped credentials or a refused address.
  • 403 api_access_inactive raises a billing alert with its reason; your integration keeps serving reads (orders, codes already bought, wallet) while the subscription is overdue, and stops placing orders until it is settled. GET /api/v1/workspace is watched for apiAccess.status and paidThroughUtc.

Ordering correctness

  • Every POST /orders and POST /orders/{id}/fulfillment carries an Idempotency-Key derived from your order identity and persisted with it; keys are never reused across purchases — they are spent for good, workspace-wide. (Orders)
  • Timeouts and 5xx on order creation retry with the same key and the same body — no check-then-resend logic.
  • 409 idempotency_conflict on an order never leads straight to a new key: the order is looked up by clientReference first, and a new key is used only once the purchase is known to be absent.
  • Request bodies are built from typed models: an unknown member is refused with 400, never ignored.
  • One-call orders set maximumTotal to the price the buyer saw, and price_above_maximum shows the buyer the new price instead of failing silently.
  • insufficient_funds alerts treasury — the wallet needs funding before orders can resume.
  • Your state machine handles every order status, treats unknown statuses as non-terminal, and only marks delivery on fulfilled; a fulfilled order can still become refunded.
  • reconciliationrequired orders (and order.under_review events) surface in your back office instead of silently aging.
  • Quotes, where you use them, are created at checkout time and never cached past expiresAtUtc; GET /quotes/{id} status decides whether one can still be ordered. (Quotes)
  • Orders are found by your own id with GET /orders?clientReference= (or by ord_ reference), order lines are reconciled by productId/variantId, and refunds by refund.

Secrets handling

  • Fulfillment is revealed late, over the dedicated endpoint, and artifact values are scrubbed from logs, APM, and error reports. (Fulfillment)
  • Each intended reveal uses a fresh Idempotency-Key; a timed-out reveal is retried under the same key within 15 minutes, fulfillment_replay_expired is handled by revealing under a new key, and idempotency_conflict (a key already bound to another reveal) is treated as a bug in key derivation, not retried.
  • The reveal allowance (remainingReveals, or GET /orders/{id}/fulfillment/allowance) is checked before spending a reveal on a user's request.
  • If codes must be stored, they are encrypted at rest with restricted access; otherwise they are delivered and dropped.
  • revealCount is tracked and an unexpected increase alerts.

Webhooks

  • Signature verification implements all four rules: raw-body HMAC, constant-time compare, ±5-minute freshness, parse-after-verify. (Webhooks)
  • The endpoint is subscribed to every event type you handle, order.under_review included, and the dashboard's Send test delivery verifies and acknowledges.
  • Deduplication by X-Xegora-Event-Id is durable (database unique index).
  • The receiver acknowledges in <2 s and defers real work to a queue; unknown event types are acknowledged and ignored.
  • Out-of-order retries are tolerated (state compared by timestamps, or re-read from the API).
  • Order events are reconciled by line productId/variantId; wallet.credited by source and movementId against the statement.
  • The xgwh_ secret is stored like a password and endpoint rotation is rehearsed.
  • A reconciliation sweep of GET /orders?updatedFrom=… (and, for treasury, GET /wallet/movements) backstops webhook gaps.

Wallet and treasury

  • Deposit networks and stablecoins are read from GET /wallet/deposit-options, never hard-coded; each deposit address you use was fetched once and pinned in your runbook together with its network and stablecoin; your treasury sends only that stablecoin on that network to it. (Wallet & Deposits)
  • Balance monitoring alerts before availableBalance reaches your burn rate.
  • The daily close reconciles the wallet statement (GET /wallet/movements) against your books; deposits match by their id and transactionHash, withdrawals by publicReference and payoutReference. (Wallet & Deposits, Withdrawals)
  • The statement walk follows next until it is absent and never stops because a page came back shorter than limit — a type-filtered page can end early with next still set; progress is measured against totalCount, not against limit.
  • Withdrawal requests carry an Idempotency-Key and the quote's fee and net; withdrawal_terms_changed re-quotes and shows the new terms.
  • GET /payout-destinations shows an approved, ready default for each currency you withdraw in before the first withdrawal.

Resilience and hygiene

  • Backoff-with-jitter retry policy for 5xx/IO; Retry-After honored on 429 and 503; nothing retries other 4xx (except 409 concurrent_update, under the same key). (Errors & Retries)
  • Scheduled jobs are spread across the minute, sized within 120 requests/min/workspace, and paced by RateLimit-Remaining; image fetching is cached (ETag) and stays within its own 300/min budget.
  • Services sharing one egress address keep concurrency modest — the per-address limits apply before keys are checked.
  • Error handling branches on code, never on message, title or detail.
  • The correlation id is logged on every response — from the X-Correlation-Id header, which every response carries, or from a problem's correlationId, which repeats it — and included in support tickets; your own X-Correlation-Id (letters, digits, ., _, :, -, at most 128) is sent where you want to trace a request end to end.
  • Amounts are parsed as decimals and timestamps as offset-aware instants; a + in a timestamp query parameter is URL-encoded.
  • HTTPS keep-alive connection reuse is enabled; client timeouts are 10–15 s.
  • Clocks are NTP-synced (webhook freshness depends on it).

When every box is checked, you are running the same patterns our own surfaces are built on. Welcome to production.


Did this page help you?