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

Seven scopes can be minted onto an API key; manage:keys is session-only and never reaches a key. Mint the narrowest one the calling code needs; Authentication says how scopes are checked and what a mismatch returns.

ScopeUnlocksOn API keys
sendPOST /v1/emails and /v1/emails/batch, cancel and reschedule; template create, update, delete; domain create, verify, tracking, delete, turn receiving on or off; 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:inboundGET /v1/emails/receiving and every route under it: received mail, its content, and attachmentsYes
read:analyticsAnalytics readsYes
read:suppressionsSuppression list readsYes
manage:webhooksWebhook endpoint CRUD, deliveries, dead letters, replayYes, live keys only (opt-in)
manage:suppressionsAdd and remove suppressionsYes, live keys only (opt-in)

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.

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. 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. For the same reason a test key cannot hold manage:webhooks or manage:suppressions: creating one returns 422 validation_error. 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 secret in a browser tab is visible to anyone who opens developer tools; see Calling the API from a browser.

Rotate on a schedule

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

Rotate from the dashboard: open API keys, click Rotate on the key, then Rotate key in the dialog. Copy the new secret from the Your new API key dialog; it is shown once. See Rotate an API key for the endpoint contract.

Revoke when you must

Revoking is immediate: the key is rejected on its very next call with 403 invalid_api_key ("This API key has been revoked."). Revoking a key that is mid-rotation ends its grace window at once. Revoking a key that is already revoked is safe and keeps the original timestamp. In the dashboard, click Revoke on the key, then Revoke key.

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.