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 returns403witherror.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
429with aRetry-Afterheader. 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.
Recommended hardening (server-to-server)
- 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-Keyheader 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-Keysent on every create/charge/send. - Webhook signatures verified.
- Rotation schedule in place; unused keys revoked.