GuidesBest Practices

Before you go live

The pre-launch checklist every integration should run through before flipping traffic to production

One page, eleven checks. Every item here exists because someone shipped without doing it and had a bad day. Run through this end-to-end before you send your first production order.

If this is your first integration, read it top to bottom. If you're iterating, jump to the section relevant to your change.

Configuration

1. Production host, production credentials

Verify that your production build reads:

  • Base URLhttps://api.octopuscards.io (not sandbox)
  • API key / API secret → your production pair, not sandbox
  • Separate environment variables from sandbox (e.g. API_KEY_PROD vs API_KEY_SANDBOX)

A smoke test: your production deployment should fail to start if any of those env vars are missing. No silent defaults.

2. Credentials in a secrets manager

API keys and webhook secrets must live in a managed secrets store - AWS Secrets Manager, GCP Secret Manager, HashiCorp Vault, Kubernetes Secrets with encrypted etcd, etc. Not:

  • .env files committed to git (even in a private repo)
  • CI variables in plaintext
  • Configuration files on disk
  • Hardcoded strings

See Security for the full threat model.

Reliability

3. Request timeouts

Every outbound HTTP call needs a timeout. Our API has its own timeouts on the server side, but your client can stall on slow connections without one.

  • Connect timeout: 5–10 seconds
  • Read/response timeout: 30 seconds (orders can take longer during provider hiccups)
  • Overall request timeout: 30–60 seconds

In Go:

client := &http.Client{ Timeout: 60 * time.Second }

4. Retries with exponential backoff

Network blips happen. Retry these error classes:

  • Network errors (connection refused, timeout, DNS failure)
  • HTTP 5xx server errors
  • HTTP 429 Too Many Requests - respect the Retry-After header if present

Do not retry 4xx errors other than 429. They're not going to succeed on the next try.

Backoff pattern: start at 200ms, double each time, cap at 10s, with jitter. 4–6 retries max.

5. Idempotency via client_reference

Every order must carry your own client_reference. It's how we detect duplicate requests if your retry logic fires twice:

{ "product_id": 123, "denomination": 50, "quantity": 2, "client_reference": "your-uuid-here" }

Rules:

  • Construct the client_reference before the first request attempt.
  • Persist it (even on the retry path) so your retries send the same value.
  • A second POST with the same client_reference is rejected with a 400 "Duplicate client_reference". Fetch the existing order via ?client_reference=<value> on the list endpoint.

Full pattern: Idempotency.

Operations

6. Webhook endpoint: verified + replay-safe

If you handle async orders or product-catalog updates, your webhook consumer must:

  • Verify the X-Signature HMAC on every request using your shared secret. See Signature Verification.
  • Reject stale events - look at X-Timestamp and drop anything older than ~5 minutes.
  • Deduplicate on X-Event-ID - we retry at least once; your handler must be safe to run twice.
  • Respond 2xx within a few seconds. Queue the heavy processing asynchronously on your side.
  • Serve only over HTTPS with a valid certificate.

7. Low-balance alerts

Prepaid wallets run out. Set up monitoring:

  • Poll GET /api/v1/wallets on a 1–5 minute cadence.
  • Alert when any wallet drops below a threshold (e.g. 20% of typical daily spend).
  • Pager-level alert at 5% - wire transfers aren't instant.

Alternative/complementary: read the wallet_id balance from order responses and trip an alert from there.

8. Observability: request IDs, structured errors, distributed traces

You will not enjoy debugging a production incident without these. Set them up before you launch, not during the postmortem.

  • X-Request-Id on every request. Generate a KSUID on your side and send it; we echo it back. Log it alongside your own correlation IDs - it's how we find your request in our logs when you open a ticket.
  • Structured error logs. Parse the { error: { name, code, message } } envelope and log error.code as a separate field, not a stringified blob. Send to your monitoring tool (Sentry, Datadog, Rollbar). Redact secrets from the request body before logging.
  • W3C traceparent header if you run any tracing at all. We accept it on every endpoint and tag our internal spans with your trace_id - so a support ticket becomes a single trace lookup instead of a forensic dig. Most OTel SDKs inject it automatically; see Observability for the Go and Node patterns.

Full guidance: Observability and Handling Errors.

Behaviour

9. Rate limit handling

  • Respect 429 Too Many Requests when it appears.
  • If the response includes Retry-After, use it - don't just back off blindly.
  • Consider request batching (larger orders instead of many small ones) to stay under the limit in the first place.

Full detail: Rate Limits.

10. Concurrent order control

If your system can fan out many orders at once:

  • Bound concurrency - limit to e.g. 10 in-flight POST /api/v1/orders per client worker.
  • Serialize per-wallet if you care about ordering - balance deductions are atomic but interleaved responses can confuse reconciliation.
  • Pre-check balance with the Charges endpoint before committing, especially for bulk campaigns.

11. Sandbox-to-prod parity

Your production deployment should behave identically to your sandbox tests, minus the host. Before you cut over:

  • Run your full test suite against sandbox once more.
  • Confirm the only diff between the sandbox and prod build is the host + credentials.
  • Deploy in a blue/green or canary pattern if your infra supports it - keep a quick rollback option for the first 24–48 hours.

Pre-launch final check

Five-minute dry run, against production credentials, in a staging environment that can reach production:

  1. POST /auth/login - returns a token within 1s.
  2. GET /api/v1/wallets - returns your funded production wallet(s).
  3. GET /api/v1/products?limit=1 - returns the live catalog.
  4. POST /api/v1/products/:id/charges - prices out a real product.
  5. (Optional) One minimal-quantity order you're prepared to let land - confirms end-to-end fulfilment. Test wallets can be topped up; the voucher is real.

If all five come back clean, you're ready.

Post-launch, first 24 hours

  • Watch your error-rate dashboard.
  • Watch the low-balance alerts.
  • Make sure at least one webhook has actually fired and been processed - send a small async order to trigger it if needed.
  • Keep your rollback path warm until you've seen real traffic behave.

On this page