Outpost logoOutpost Docs

API Keys & Authentication

How requests to Outpost are authenticated and scoped.

Every request to Outpost (except the public opt-out page) is authenticated with an API key sent in the x-api-key header.

In the dashboard: keys are issued and revoked from a project's API Access section, see Managing Tenants & Projects.

How they relate

API keys are owned by projects, and the tenant/project a key belongs to is stamped onto every message it sends - which is how messages, stats, and webhooks all end up correctly scoped.

Two kinds of keys

Admin keyTenant key
ScopeEntire systemOne tenant + one project
Created viaBy hand (deliberately no API)POST /projects/:projectId/api-keys
Can specify tenantId/projectId in requestsYesNo - returns 403
Typical userOutpost dashboard, operatorsDownstream services

Key facts

The plaintext key is shown exactly once. Creating a key returns plainApiKey a single time. Outpost stores only a SHA-256 hash - if the plaintext is lost, create a new key. Keys look like outpost-<token>, so they're recognizable in config files without revealing their scope.

Validation is a single fast lookup. For a tenant key to be valid: the key must exist, be active, not be expired, and both its tenant and project must be active. Tenant/project status is denormalized onto the key record (tenantIsActive, projectIsActive), so disabling a tenant takes effect immediately without adding database reads to the authentication hot path.

Failures are distinct. An invalid/expired/deactivated key returns 401; a valid key used on an endpoint its role doesn't allow returns 403.

Keys can expire. An optional expiresAt can be set at creation. Expired keys remain in the system but stop working.

Limits: a project can have at most 10 API keys.

The one unauthenticated surface

The public messaging status page (GET/POST /o/:token) is intentionally unauthenticated so message recipients can manage their opt-out state from a link. It is rate-limited and tokens are unguessable - see Phones & Opt-Out.

API reference

Calling from TypeScript? See Generating a Typed Client.

On this page