GuidesBest Practices

Security

Credential handling, transport, IP allowlisting, webhook signing, and secrets rotation

A B2B API integration is a set of secrets and a set of trusted endpoints. Get both right, and the hard part is done.

This page is the operator's mental model. For implementation details, each subsection links to the relevant reference or guide.

Transport

  • HTTPS only. Every endpoint is served over TLS 1.2+. HTTP requests are rejected at the edge. Never pin certificates - we rotate them on a standard cadence.
  • No wildcards in Host. Always call the canonical host (https://api.octopuscards.io / https://sandbox-api.octopuscards.io). Custom routing (reverse proxies, internal DNS mirrors) should preserve the original hostname for SNI.
  • Verify server certs. Don't disable cert verification in HTTP clients, not even in dev. Use proper CA trust.

Credentials

Your API key + API secret are the long-lived root of trust. Everything else (JWTs, webhook payloads) derives from them.

Storage

  • Secrets manager, always. AWS Secrets Manager, GCP Secret Manager, Vault, Kubernetes secrets with encrypted etcd - anything that provides encryption at rest, access auditing, and rotation hooks.
  • No plaintext in git. Not in .env.example, not in docker-compose.yml, not in config files.
  • No plaintext in logs. Log redaction should cover the username/password fields of /auth/login requests at minimum. Structured loggers (zap, zerolog, pino) make this easy.

Access

  • Restrict who can read the production secret - contractors, summer interns, and read-heavy dashboards don't need it.
  • Use separate credentials for sandbox and production. Never "temporarily" use prod keys for development.
  • Separate credentials per client account, per environment. Don't share one pair across multiple integrations.

Rotation

Rotate the API secret:

  • On suspected leak. Committed to git, seen in a screenshot, left in a departing employee's password manager - rotate.
  • On staff change. Any employee with production secret access rotates when they leave.
  • On a schedule. Annual rotation is a sensible default. Quarterly if your compliance posture requires it.

Flow: request a new secret → deploy it → verify it works with a /auth/login call → ask support to revoke the old one. JWTs derived from the old secret expire within an hour, so no user-facing interruption.

See Create your API key for the onboarding flow.

Tokens

  • Access tokens (1h lifetime). Stored in process memory, not on disk. Refresh proactively at ~80% lifetime (48 minutes) to avoid races.
  • Refresh tokens (7d lifetime). Should also live in process memory. If you must persist them (e.g. across process restarts), encrypt them with a KMS-managed key - treat them like the API secret.
  • Token rotation on refresh. Each POST /auth/refresh issues a new refresh token and revokes the old one. Storing more than the current refresh token wastes space and creates risk.
  • No tokens in URLs. Never ?token=…. They end up in access logs, Referer headers, and browser history. Use the Authorization: Bearer … header.

Network controls

IP allowlist

Optional but strongly recommended in production. Share your egress CIDR ranges with your account manager; any request from an address outside the list returns 403 Forbidden.

Appropriate when:

  • Your API traffic originates from fixed infrastructure (VPC NAT gateways, dedicated IPs, bare-metal).
  • You're willing to coordinate small changes to your network with us.

Skip when:

  • You call from ephemeral public IPs (generic Lambda without a NAT, scheduled jobs on shared runners).
  • You're in early dev and network setup hasn't stabilised.

Webhook origin

Webhooks we send you originate from a known set of IPs. Share with your account manager if you want to allowlist inbound as well as outbound - but the primary security control on inbound webhooks is the HMAC signature, not the source IP.

Webhooks

Every webhook we deliver is signed with HMAC-SHA256 using a shared secret. Your webhook consumer must:

  • Verify the X-Signature header on every request.
  • Use a constant-time comparison (hmac.Equal in Go, crypto.timingSafeEqual in Node, hmac.compare_digest in Python) - never == on strings.
  • Reject stale events - the X-Timestamp header lets you drop anything older than ~5 minutes.
  • Deduplicate on X-Event-ID - we retry at least once.

Full implementation: Signature Verification.

Webhook secret rotation

Rotate the webhook secret under the same conditions as the API secret. During rotation we can issue a second secret that's valid in parallel; your verifier checks against both until you've switched everything over.

Logging & observability

  • Redact secrets from structured logs. At minimum: username, password, Authorization header values.
  • Log X-Request-Id on every request. It's how support finds your call in our logs.
  • Retain request IDs for at least 30 days. Most investigations happen within a week; 30 days gives generous margin.
  • Don't log webhook payloads verbatim if they contain PII. Redact or hash identifying fields.

Incident response

If you suspect a leaked credential:

  1. Rotate the compromised secret immediately (API or webhook).
  2. Open a support ticket with your best timeline of when the leak happened and any unusual activity you've seen.
  3. Review orders placed since the suspected leak - cross-reference your own records.
  4. Post-mortem your credential handling - how did it leak, and what prevents it next time?

For anything sensitive, escalate directly to your account manager rather than the normal support queue. Include the X-Request-Ids of any suspicious API calls.

Threat-model quick reference

ThreatPrimary controlSecondary control
API secret leaksSecrets manager + rotationIP allowlist
JWT interceptionHTTPS only + Bearer in headerShort 1h lifetime
Replay of webhookX-Event-ID deduplicationX-Timestamp staleness check
Tampered webhook payloadHMAC-SHA256 signatureHTTPS-only webhook URL
Unauthorized IPIP allowlistAccount-level audit via X-Request-Id
Insider misuse of production credsLeast-privilege access to the secrets managerAudit log on secret reads

Summary

  • TLS everywhere, Bearer tokens in headers, no plaintext in git.
  • Secrets in a managed store. Rotate annually and on any leak suspicion.
  • IP allowlist for production when feasible.
  • HMAC-verify every webhook in constant time.
  • Log X-Request-Id; redact secrets.

On this page