API Key Rotation
An API key that stays unchanged for years eventually ends up somewhere you didn't intend -- a log file, a screenshot, an old repository. Rotating your keys on a schedule limits how much damage a leaked key that nobody noticed can still do.
Rotation creates a new key and lets the old one keep working for a limited overlap window, so you can roll the new key out without downtime.
When to rotate
- Routinely, every 90 days. This is the recommended baseline for production keys.
- Immediately, whenever you suspect a key may have leaked -- committed to a repository, pasted into a support ticket, visible in a screenshot. Don't wait for the next scheduled rotation.
How it works
Rotation is a four-step process:
- Trigger the rotation. In the Dashboard, go to API Keys and choose Rotate on the key you want to replace. You can also call the rotation endpoint directly (see below). Both the old key (the predecessor) and the new key (the successor) work during the overlap window that follows.
- Roll out the new key to whatever service or script used the old one. The successor inherits the predecessor's name, scopes, and environment, so no configuration changes beyond swapping the key value are needed.
- Check the "Last used" column for the old key once the rollout is complete. This is how you confirm the migration actually worked: if the column keeps advancing, something is still calling the API with the old key, and you should track it down before the window closes.
- Let the window expire. Once you've confirmed the old key is idle, there's nothing further to do -- it stops working automatically at the end of the overlap window. You don't need to revoke it manually.
Don't guess -- check 'Last used'
"Last used" is the only reliable signal that a rotation is safe to let finish. Rotating and immediately walking away means finding out about a forgotten integration only after it starts failing.
Triggering a rotation via the API
overlap_days is optional and accepts 1-90; if you omit it, the platform
default applies (query it via GET /api/v1/api-keys/rotation-defaults). The response contains the successor's plain-text
key and a rotated_from block describing the predecessor -- this is the only
time the new key's plain text is shown, so store it the same way you would a
freshly created key.
The successor also inherits the predecessor's expiration: if the predecessor had a fixed lifetime (say, 90 days from when it was created), the successor gets the same duration counted from the moment of rotation. If the predecessor never expired, the successor doesn't either.
What the response headers tell you
Once a key has an expiry date that hasn't passed yet, every response authenticated with it carries three headers:
| Header | Meaning |
|---|---|
Sunset |
The date the key stops working, as an HTTP date |
Deprecation |
Always true while the key carries an expiry date in the future -- whether or not it was rotated |
X-API-Key-Expires-In-Days |
Days remaining until the key stops working |
All three are readable from browser clients as well (they're on the CORS-exposed header list), so a frontend integration can surface the warning without a server-side proxy.
If you see these headers on a key you didn't expect to expire, check the Dashboard -- it may already have been rotated.
Notification
By default three days before a key expires, SubscribeFlow sends one reminder email to each of the organization's Owners (the lead time is configurable server-side, and the run happens once a day). It's a backstop, not a replacement for checking "Last used" yourself -- by the time it arrives you should already know whether the rollout succeeded.
Limits
- The plain-text key is shown exactly once -- at creation and again at rotation. If you lose it, rotate the key to get a new one; there's no way to retrieve a lost key's plain text.
- A key can be rotated exactly once while its successor exists. If you need to rotate again, rotate the successor, not the original key. Attempting to rotate an already-rotated, revoked, or expired key returns an error.
- The window can come out shorter than requested. If the predecessor
already carried an expiry date, the earlier of the two wins: asking for
overlap_days: 30on a key that expires in two days gives you two days. Therotated_fromblock in the response states the actual expiry. - The overlap window is fixed at the moment of rotation and can't be
extended afterwards. There's no way to give the predecessor more time
once the rotation has happened -- rotating the successor doesn't change the
predecessor's expiry either. If you expect a longer rollout, choose a
larger
overlap_daysvalue when you trigger the rotation.