Authentication & limits
API keys, SDK keys, session cookies, HMAC ingest signatures, rate limits and the error format.
Updated 2026-09-02
Credentials#
| Credential | Format | Used by | How to send |
|---|---|---|---|
| API key | wc_live_ + 32 chars | REST API (/api/v1/*, /postback), MCP server, edge warm-up | Authorization: Bearer wc_live_… or ?api_key= (postback URLs only) |
| SDK key | sk_… per app | Mobile / web / CTV SDK endpoints (/api/sdk/v1/*) | X-SDK-Key: sk_… header (or ?sdk_key=) |
| Session | wc_session JWT cookie | Dashboard and /api/dashboard/*, /api/protect/export, /api/protect/sync | Browser only |
| Ingest signature | hex HMAC-SHA256 of the raw body | POST /api/ingest/clicks (edge worker) | X-WC-Signature header, secret EDGE_INGEST_SECRET |
API keys are hashed with SHA-256 at rest; only the prefix is stored in clear. Revoked keys fail immediately. Every key belongs to exactly one organization; there are no cross-organization reads.
Create one key per integration (for example google-ads-offline-import, claude-mcp) so you can revoke them independently. The audit log records which key performed each write.
Rate limits#
Limits are applied per client IP (IPv6 aggregated at /64) per minute at the Next.js proxy, and additionally per key in Redis and at the Cloudflare edge when deployed.
| Path | Limit / minute |
|---|---|
/api/sdk/* | 600 |
/api/protect/beacon | 600 |
/api/v1/* | 300 |
/postback | 300 |
/api/mcp | 240 |
/api/skan/* | 120 |
/login, /register | 20 |
A limited request returns 429 with a Retry-After header and body {"error":"rate_limited","retry_after":<seconds>}. Redirect endpoints (/c, /l, /i) are not rate limited here; the edge worker applies burst protection there.
Error format#
Validation and auth errors are JSON:
{ "error": "unauthorized" }{ "error": { "formErrors": [], "fieldErrors": { "value": ["Too small: expected number to be >=0"] } } }See Errors & idempotency for the full status-code table.
CORS#
SDK endpoints and the beacon answer OPTIONS with Access-Control-Allow-Origin: * so web and React Native apps can call them directly. The REST API is server-to-server; do not embed API keys in browsers.
Security headers#
Every response carries X-Content-Type-Options: nosniff, X-Frame-Options: DENY and Referrer-Policy: strict-origin-when-cross-origin. Redirect responses add Cache-Control: no-store.