Skip to main content
Foil uses Authorization: Bearer <token> for every authenticated request. The token format signals what the key is for and what it can do.

Key types

Two distinct credential types exist on the public API. Each has a different prefix, lives in a different place, and authorizes a different set of endpoints. A few endpoints need no key: GET /v2/collect/pings/:pingId and POST /v2/collect/network-checks.
Secret keys (sk_*) have read access to your session history and visitor fingerprint data, and depending on their scopes they can manage your API keys and webhooks. Never commit one to git, send it over a non-TLS channel, or ship it in a client bundle. If a secret key is ever exposed, rotate it immediately.

Using a key

Send the key as a Bearer token on every request.
If the Authorization header is missing, the API returns 401 with auth.missing_api_key. If the header is malformed, or the key is unrecognized, revoked, or past the end of its rotation grace window, the API returns 401 with auth.invalid_api_key.

Scopes

Publishable keys are limited to the Collect API, and secret keys are limited by scope. The endpoint decides which key type it accepts, and for secret keys it also decides which scope the key must carry.

Publishable keys (pk_*)

A publishable key can call only the Collect API endpoints that the SDKs need:
  • POST /v2/collect/sessions - open or resume an encrypted unified session
  • POST /v2/collect/sessions/:sessionId/documents/:documentId/batches - stream observation batches
  • POST /v2/collect/sessions/:sessionId/documents/:documentId/results - mint a sealed handoff for the current session
A publishable key can’t read verdict data, list sessions, inspect fingerprints, or reach any management endpoint. Calling those endpoints with a publishable key returns 403 with auth.secret_key_required. There is no escalation path from pk_* to sk_* - the keys are fully partitioned.

Native publishable keys

Each publishable key has a client_target: browser (the default), native-ios, or native-android. A native key works only with the matching Foil mobile SDK on the /v2/collect/sessions endpoints. Any other use of a native key, including a request from a different client or to a different path, returns 403 with auth.native_key_not_allowed. Origin restrictions apply to browser keys only, and native keys can’t define allowed origins.

Secret keys (sk_*)

Each secret key carries a list of scopes, and each endpoint requires one scope. A request to an endpoint whose scope the key doesn’t carry returns 403 with auth.insufficient_scope, and the error message names the missing scope. The scope * grants all current and future scopes. A secret key with no stored scopes receives the default scopes sessions:read, sessions:update, and fingerprints:read. The defaults don’t include sessions:list or fingerprints:list, so add those scopes if your integration lists sessions or fingerprints. Foil accepts secret keys only on the REST endpoints in this table. Calling a Collect API endpoint with a secret key returns 403 with auth.secret_key_not_allowed. The /v1/organizations/:organizationId/... endpoints work only for the organization that owns the calling key. A request for any other organization returns 403 with auth.organization_access_denied. See Organizations for the full list of scopes. Secret keys on test mode (sk_test_*) and live mode (sk_live_*) authorize the same endpoints but operate on separate data.

Live and test environments

Each organization has separate live and test key pairs. They share the same REST surface but are fully isolated:
  • Sessions minted under pk_test_* cannot be read back with sk_live_*, and vice versa.
  • Organization, billing, and member state is the same across both environments. Sessions and fingerprints are separate.
  • Test-mode sessions may expose richer debug detail in internal tooling and the dashboard; the public browser handoff stays opaque regardless of environment.
Use test mode for automated integration tests, development, and CI. Use live mode when you ship.

Origin restrictions

Browser publishable keys can be narrowed to a list of allowed origins in the dashboard or through the API keys endpoints. A pk_live_* limited to https://yourdomain.com is rejected with auth.origin_not_allowed when presented from any other origin, or without an Origin header, so a leaked key is only as dangerous as the origins it’s allowed to speak for. A key with no allowed origins accepts any origin. Restrict every production publishable key to the exact hostnames you load t.js from. Allow wildcards only on test keys.
Secret keys are not origin-restricted - they’re meant for server-to-server traffic where the caller’s “origin” is your infrastructure. Control where they’re used by keeping them on the server side and not shipping them into the browser.

Rotating a key

Rotation issues a new key with the same type, environment, name, origins, client target, and scopes. The old key keeps working for a 24-hour grace window. This lets you roll new secrets into deployment before the old one stops - no flight of 401s during the overlap.
During the grace window the old key has the status rotating, and its grace_expires_at field shows when it stops working. When the window ends, Foil revokes the old key automatically. Recommended rotation flow:
  1. Call the rotation endpoint. You receive the new key material in the response.
  2. Deploy the new key to all environments that use the old one.
  3. Verify the new key is in use (e.g. by inspecting logs or a dashboard request count).
  4. Optionally revoke the old key without waiting for the grace window to end.
See API keys for the full request and response shapes.

Revoking a key

Revocation is unconditional, but it isn’t instantaneous. Foil caches successful key verifications for up to about 15 seconds, so a revoked key can keep working for that long. Changes to a key’s scopes or allowed origins take the same time to apply.
If you believe a secret key has been exposed, revoke first and investigate second. The rotation flow above is for planned, graceful transitions; revocation is for incidents.

Rate limits

Foil applies rate limits per organization and key type. The limit belongs to the organization and key type, not to an individual key. By default, all of an organization’s secret keys share one limit of 600 requests per 60-second window, and all of its publishable keys share one limit of 3000 requests per 60-second window. Foil support can configure organization-level overrides when a production integration needs more headroom. Responses carry X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Cost headers. Rate-limited responses are 429 Too Many Requests and include a Retry-After header. See Rate limits for how the window works, what counts against it, and how to retry.

What’s next

Errors

Error envelope shape, status codes, and retry semantics.

Pagination

How list endpoints paginate and filter.

Security and privacy

How keys are protected end-to-end.

Sessions

First authenticated endpoint most integrations hit.