Mailfully logo

A leaked API key

Contain an exposed Mailfully key: rotate it, understand the grace window on the old secret, and audit what it could reach.

Decide whether to rotate or revoke before you touch the key. Rotation starts a 24-hour grace window that revoking afterward cannot shorten, so doing the two in the wrong order leaves a hostile exposure live for the full 24 hours regardless. Then audit what the key could reach.

Decide: rotate or revoke

SituationAction
Accidental commit in a private repo you controlRotate: the 24-hour overlap lets you redeploy without an outage
Public repo, published client bundle, or any exposure you cannot boundRevoke first, and don't rotate first: revoking an active key ends it immediately, but revoking a key that already carries a rotation's future-dated revoked_at just keeps that existing timestamp

Rotation stamps the old key with a future-dated revoked_at to start its grace window. Revoke on a key that already has a revoked_at for any reason, including that grace window, and revoke keeps the existing timestamp rather than moving it earlier: revoke never shortens or extends a rotation grace window. So for a public or hostile exposure, revoke first.

Rotate immediately, for a bounded exposure

Rotation mints a successor that copies the old key's name, scopes, domain, and environment, and returns the new plaintext once. Rotate from the dashboard's API keys page:

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

Rotation requires manage:keys, a session-only scope: every mf_ API key receives 403 here, so the request has to carry a dashboard session token, never mf_live_. Only a key with no revoked_at can be rotated; a revoked or already-mid-grace key returns 409. See Rotate an API key.

Understand the grace window

Rotation doesn't kill the old secret instantly. It keeps authenticating for 24 hours so a deploy can overlap, then fails with 403 invalid_api_key ("This API key has been revoked."). For a hostile exposure, that 24 hours is 24 hours of someone else sending as you, which is exactly why the decision above has to happen first.

Revoke for a public or hostile exposure

Revocation ends the old secret immediately, at the cost of breaking anything still using it, provided the key is still active when you revoke it. That's the right call for a public repo, a published client bundle, or any exposure you can't bound.

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

Also manage:keys, so also a dashboard-session action. See Revoke an API key.

Audit what it could do

Read the key's scopes and its domain_id. A domain_id restricts the key to that one domain; without one, a send key can send from any of your verified domains. Check for sends you don't recognize and for a volume spike, both reachable with an API key since they need only read:emails and read:analytics:

curl "https://api.mailfully.com/v1/emails?limit=100" \
  -H "Authorization: Bearer mf_live_xxxxxxxxxxxx"

See GET /v1/emails and Analytics for the daily rollup.

Then clean up the exposure

Rotating or revoking the key doesn't remove the secret from git history or from a log aggregator. Treat those as separate cleanup.

Prevent the next one

Keep keys in environment variables rather than in source, use separate test and live keys, and scope each key as narrowly as the task needs. API key security covers where to store a key's secret and how to keep scopes narrow.