Authentication
Pushrail authenticates two distinct callers: your servers, which send events and manage destinations, and your end-customers' browsers, which use the embedded portal to manage their own destinations. Servers use long-lived API keys; browsers use short-lived embed sessions minted by your backend. The two systems never share credentials.
API keys
API keys are created per workspace in Settings → API keys. Each key is bound to a single environment (Development, Staging, or Production) and a single scope. The full secret is shown once at creation; afterward only a salted hash is stored, so a leaked key cannot be recovered from the dashboard, only rotated.
Send the key as a bearer token:
curl https://api.pushrail.io/events \
-H "Authorization: Bearer pr_pro_REDACTED" \
...
Both the Node and Python SDKs read the key from their constructor option (apiKey / api_key); load it from your secret store, not your repo.
MCP API keys (for agents and the CLI)
The API keys above are for sending events and managing config from your own servers — each is bound to one environment, and the prefix encodes it. They are not what you use to connect an AI agent or the CLI.
For that, mint an MCP API key from MCP API keys in the dashboard. MCP API keys are account-wide — tied to your workspace, not to a single environment — and begin with pmk_. They carry a read or manage scope, and the secret is shown once at creation just like an environment-scoped key. When an agent connects with one, it names the target environment on each action rather than having it fixed by the key. See MCP integration → MCP API keys for the full walkthrough.
Use an environment-scoped key when your backend code emits events; use an MCP API key when you're connecting an agent, the CLI, or the MCP server.
Environments
Every workspace ships with three environments: Development, Staging, and Production. They share the destination catalog and member permissions but not data, events, destinations, routing rules, deliveries, and keys are all environment-scoped. The prefix on a key (pr_dev_, pr_sta_, pr_pro_) makes the environment obvious in any log line or pull request.
Use Development for local emit calls, Staging for end-to-end tests against real destinations you control, and Production for the real thing. Promoting a destination across environments is a manual config copy on purpose, it forces a human review when a customer-owned URL or credential changes.
Scopes
Keys carry one of three scopes. Ingest can only call the ingestion endpoints, this is the scope you embed in your product server. Manage can call any control-plane endpoint (destinations, routing rules, transforms, replays, audit) and is intended for backend automation that manages tenant config. Read is manage minus writes, useful for monitoring scripts.
Prefer the narrowest scope that does the job. An ingest-only key cannot be used to read deliveries or to mutate destinations, even if it leaks, the blast radius is bounded to event injection.
Rotation
Rotate a key from Settings → API keys → ⋯ → Rotate. Pushrail returns the new secret and keeps the previous key valid for a 24-hour grace window so you can deploy the new value without a delivery gap. After 24 hours the old key returns 401. The rotation is logged in the audit trail with actor, timestamp, and the previous key fingerprint.
For signing-secret rotation (the secret Pushrail uses to sign outbound webhook bodies), the same 24-hour dual-valid window applies on the receive side, see Verifying webhook signatures for the dual-verify pattern.
Embed sessions
The embedded portal, the iframe your customers use to manage their own destinations, does not use API keys. Your backend mints a short-lived embed session token by calling the embed session endpoint with a manage-scoped API key, passing the customerExternalId of the end-customer who is logging in. The returned token is good for a few minutes; you hand it to the client, which passes it to the portal iframe.
See Embedding the portal for the end-to-end pattern. The session model means you never hand long-lived credentials to a browser, and revocation is implicit, sessions expire on their own.