Mailfully logo

API key security

Choose the narrowest scope, keep test and live keys apart, store secrets outside your repository, and rotate on a schedule.

A Mailfully API key is a bearer credential: whoever holds the secret can do everything the key's scope allows.

Scope every key down

Four scopes can be minted onto an API key; the three manage:* scopes are session-only and never reach a key. Mint the narrowest one the calling code needs.

ScopeUnlocksOn API keys
sendPOST /v1/emails and /v1/emails/batch, cancel and reschedule; template create, update, delete; domain create, verify, tracking; also satisfies email, template, and domain readsYes (the default)
read:emailsGET /v1/emails, GET /v1/emails/{id}, GET /v1/emails/{id}/events; also template and domain readsYes
read:analyticsAnalytics readsYes
read:suppressionsSuppression list readsYes

An analytics dashboard that only renders charts needs read:analytics and nothing else: mint that scope alone rather than reusing a send key that could also dispatch mail. See Authentication for how scopes are checked and what a scope mismatch returns.

Keep test and live apart

A key's secret starts mf_live_ or mf_test_, and that prefix partitions your message data: messages, events, analytics rollups, and the keys themselves are scoped to both your org and the key's environment. A test key cannot read or send as live. The partition covers data, not configuration: templates, domains, webhooks, and suppressions are shared by both environments, so a leaked test key carrying the default send scope can still change live templates and domains. Test and live environments says exactly where the boundary falls, and Test mode covers how test sends behave.

Where to keep the secret

Store a key's plaintext in an environment variable or a secret manager, and load it from there at runtime. Never commit it to source, never embed it in a client-side bundle, and never write it to a log line: a value that reaches a browser tab is exposed to anyone who opens developer tools, which is the exact failure Calling the API from a browser walks through.

Rotate on a schedule

Rotating mints a new secret and keeps the old one authenticating for a 24-hour grace window, so a deploy that carries the new secret can overlap with instances still running the old one. After the window closes, the old secret fails with 403 invalid_api_key ("This API key has been revoked.").

Rotation requires manage:keys, which is a dashboard-session scope no API key can hold, so it runs from the dashboard's API keys page rather than from your application. A curl call against the endpoint authenticates with a session token, not an API key:

curl -X POST https://api.mailfully.com/v1/api-keys/key_01J1F8Z3N9WXYQ5T2V4B6C8D0E/rotate \
  -H "Authorization: Bearer <session token>"

See Rotate an API key for the full request and response contract.

Revoke when you must

Revoking is immediate rather than graced: an active key is stamped revoked_at right away and rejected on its very next call, with 403 invalid_api_key ("This API key has been revoked."). Calling revoke again on an already-revoked key is safe and keeps the original timestamp.

If a key leaks

Whether to rotate or revoke first, and in which order, changes how long an exposed key stays live. See A leaked API key for the containment steps.