Skip to main content

Security model

The API is designed so a leaked or misused key causes the least possible damage, and so one company can never reach another company's data. Anyone can probe a public API — these controls are what make it safe to expose.

Transport

  • TLS 1.2+ required. Plain HTTP is refused; HSTS is enforced.

Credentials

  • Client ID + Client Secret per key. The secret is generated from a CSPRNG, returned once, and stored hashed (SHA-256). Verification is constant-time.
  • Secrets are never logged, never returned by any endpoint, and never shown again.

Least-privilege scopes

  • Each key carries explicit scopes (e.g. invoices:read, customers:write). Calling an endpoint whose scope the key lacks returns 403 with error.code = insufficient_scope.
  • Create read-only keys for reporting; only grant :write (and messaging :send) when needed.

Per-key rate limits

  • Each key has a requests-per-minute limit (default 60). Exceeding it returns 429 with a Retry-After header. Messaging has separate, lower limits + a daily cap per channel.
  • See Rate limiting.

Browser / front-end use

  • A browser key must set allowed origins (CORS allow-list); other origins are rejected. Wildcard * is discouraged for any key that can write or send.
  • For anything beyond public read endpoints, proxy through your backend so the secret stays server-side.
  • IP allow-list the key to your server egress IPs.
  • Short expiry + scheduled rotation.
  • One key per integration, so you can revoke one without affecting others.

Tenant isolation

  • A key is bound to one company. The server sets the tenant context from the key on every request; queries cannot cross into another company's data. No parameter can change the company a key operates on.

Writes & idempotency

  • All writes accept an Idempotency-Key header so retries can't double-create. See Idempotency.

Webhooks

  • Outbound webhooks are HMAC-SHA256 signed — verify the signature before trusting a payload. See Webhooks.

Security checklist

  • Secret stored server-side (env / secret manager) — never in client code or git.
  • Key scoped to the minimum resources + actions needed.
  • Browser keys restricted by allowed origins; secret never exposed to the browser.
  • Idempotency-Key sent on every create/charge/send.
  • Webhook signatures verified.
  • Rotation schedule in place; unused keys revoked.