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 indocker-compose.yml, not in config files. - No plaintext in logs. Log redaction should cover the
username/passwordfields of/auth/loginrequests 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/refreshissues 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,Refererheaders, and browser history. Use theAuthorization: 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-Signatureheader on every request. - Use a constant-time comparison (
hmac.Equalin Go,crypto.timingSafeEqualin Node,hmac.compare_digestin Python) - never==on strings. - Reject stale events - the
X-Timestampheader 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,Authorizationheader values. - Log
X-Request-Idon 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:
- Rotate the compromised secret immediately (API or webhook).
- Open a support ticket with your best timeline of when the leak happened and any unusual activity you've seen.
- Review orders placed since the suspected leak - cross-reference your own records.
- 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
| Threat | Primary control | Secondary control |
|---|---|---|
| API secret leaks | Secrets manager + rotation | IP allowlist |
| JWT interception | HTTPS only + Bearer in header | Short 1h lifetime |
| Replay of webhook | X-Event-ID deduplication | X-Timestamp staleness check |
| Tampered webhook payload | HMAC-SHA256 signature | HTTPS-only webhook URL |
| Unauthorized IP | IP allowlist | Account-level audit via X-Request-Id |
| Insider misuse of production creds | Least-privilege access to the secrets manager | Audit log on secret reads |
Summary
- TLS everywhere,
Bearertokens 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.