Flag Expiration Dates
Most feature flags are meant to be temporary. An expiration date makes that plan explicit: set the day you expect a flag to come out, and Featureflip reports it as stale once that day passes, so it turns up in cleanup work instead of sitting forgotten for months.
An expiry date works as a reminder. Passing the date never changes what the flag serves. See What happens when the date passes below.
A project can also set a default lifetime, so the temporary flags nobody got around to dating still get an end date.
Expiration dates and default lifetimes are available on every plan, Free included.
Setting an expiry date
Section titled “Setting an expiry date”When you create a flag
Section titled “When you create a flag”The Create Flag page includes an optional Expires on field. Pick a date and the field explains what it does: past this date, the flag is reported as stale, and evaluation does not change. Leave it blank for a flag with no planned end date.
On an existing flag
Section titled “On an existing flag”Open the flag and look at its header. A flag with no expiry shows Set expiry: click it, pick a date, and save. A flag that already has one shows Expires followed by the date, with an Edit expiry control next to it.
You can’t set or change an expiry on an archived flag.
Changing or removing an expiry date
Section titled “Changing or removing an expiry date”From the same header control:
- Change the date. Click Edit expiry, pick a new date, and Save.
- Remove it entirely. Click Edit expiry, then Remove. Removing is always allowed, even after the date has passed, so you can back out of a commitment you no longer want to make.
Setting an expiry through the API
Section titled “Setting an expiry through the API”Expiry has its own endpoints, separate from the main flag update call. That way, other tools that write to your flags, including the Terraform provider, never clear a date by accident just because they don’t know the field exists.
Set the date at creation:
POST /api/v1/orgs/{org}/projects/{project}/flags{ "key": "checkout-redesign", "name": "Checkout Redesign", "type": "Boolean", "expiresAtUtc": "2026-12-01"}Or set it on an existing flag:
PUT /api/v1/orgs/{org}/projects/{project}/flags/{flag}/expiry{ "expiresAtUtc": "2026-12-01"}A date on its own means the end of that day in UTC, exactly what the dashboard sets when you pick it. You can send a full timestamp such as 2026-12-01T17:00:00Z instead, and it’s stored as written.
Clear it with:
DELETE /api/v1/orgs/{org}/projects/{project}/flags/{flag}/expirySee the API Reference for the full request and response shapes, and Authentication for how to get a token.
Setting an expiry from an AI agent
Section titled “Setting an expiry from an AI agent”The MCP server has a set_flag_expiry tool that sets or clears the date, and create_flag takes an expiresAtUtc option, so you can ask Claude Code or Cursor to date a flag while it writes the code behind it. find_stale_flags lists every expired flag, however recently someone edited it.
A default lifetime for a project
Section titled “A default lifetime for a project”Not every temporary flag gets a date. Someone ships a release flag, means to add one later, and never does. A project’s default lifetime covers those flags. Set it to 90 days and every temporary flag in the project with no date of its own expires 90 days after it was created.
The default is never written onto your flags. Featureflip works out each flag’s expiry when it needs it, in this order:
- A permanent flag never expires.
- A flag with its own expiry date uses that date, whether it falls before or after the default.
- Any other flag expires once the project’s default lifetime has passed since the day it was created.
- With no default set, a flag without a date of its own never expires.
Setting the default
Section titled “Setting the default”Open Project Settings from the project switcher and find the Default flag lifetime card. Enter a whole number of days from 1 to 3650 and click Save. To remove the default, empty the field and save. Changing it takes the Member role or above.
While you type, the card previews what the new value would do: “12 temporary flags will be marked past expiry within the hour”, say, or “3 temporary flags will no longer be past expiry”. Read that line before saving. The lifetime counts from each flag’s creation date with no grace period, so a 30-day default on a project full of year-old flags marks every one of them expired at once. On Team and above their owners then hear about it in the next daily cleanup notice.
A new value takes effect at the next hourly staleness run. Adding a default or shortening it marks flags expired, and lengthening or removing one clears the flags that no longer qualify. Each change is recorded in the audit log and carried in the project.updated webhook event.
How a flag shows it
Section titled “How a flag shows it”A flag whose date comes from the default shows Expires or Expired, the date, and “(project default)” in its header. Set expiry stays right next to it, since a date of the flag’s own replaces the default for that flag. In the flags list, the Expired marker’s tooltip reads “Past the project’s default flag lifetime”. The cleanup notice email says “Passed the project’s 90-day default lifetime on 2026-12-01” where a flag with its own date would say “Expired on 2026-12-01”.
Removing a flag’s own date hands it back to the default. If the default has already run out for that flag, it stays expired.
Setting the default through the API
Section titled “Setting the default through the API”PUT /api/v1/orgs/{org}/projects/{project}/default-flag-lifetime{ "days": 90}Send {"days": null} to remove it. days is required, so a JSON body without it ({}, a misspelled field name, or an empty body) is refused with a 400, and a request with no JSON body at all gets a 415. A typo or a body lost along the way can’t wipe the setting by accident. The project’s defaultFlagLifetimeDays field reports the current value.
To preview a change before making it:
GET /api/v1/orgs/{org}/projects/{project}/default-flag-lifetime/impact?days=30The response carries newlyPastExpiry and noLongerPastExpiry, the same two counts the dashboard shows. Leave out days to preview removing the default.
Flags returned by the API gain two read-only fields. effectiveExpiresAtUtc is the date the flag actually expires, from its own date or the project default. expirySource says which one that is: explicit, project-default, or null when the flag has no expiry. expiresAtUtc keeps its old meaning, the date someone set on the flag itself. The MCP server’s find_stale_flags reads the effective date as well, so a flag past the project default comes back expired, with expirySource: project-default.
Validation
Section titled “Validation”- The date must be in the future when you set it. A past or missing date is refused. Today counts, because the flag doesn’t expire until the day ends. A date you set earlier that has since passed stays stored and valid. Nothing forces you to touch it the moment it expires.
- Dates apply as whole UTC days. Whatever date you pick is treated as a UTC calendar day, and the flag expires at the very end of that day in UTC. This keeps the date consistent no matter which timezone you or a teammate happens to be in. A date sent through the API or the MCP server works the same way. The Terraform provider only takes a full timestamp, so write
2026-12-01T23:59:59Zthere to get the same result. - Removing an expiry is never date-checked. You can clear an already-expired date at any time.
- A default lifetime is a whole number of days from 1 to 3650. Removing it is always allowed.
- A permanent flag can’t have an expiry date. A flag marked permanent is meant to stay, so a date is refused. Make it temporary first.
What happens when the date passes
Section titled “What happens when the date passes”Nothing about evaluation changes. The flag keeps serving exactly what it served the day before: the same rules, the same rollout percentage, whatever targeting was already in place. An expiry date tells your team when a flag is meant to come out. Want Featureflip to actually flip a flag on or off at a set time? Use scheduled changes for that.
What does change is how the flag is reported:
- It’s added to stale flag detection with the reason
PastExpiry, always at the Stale tier, whether or not the flag has any real traffic. - The flag detail header switches from Expires to Expired, shown in amber.
- The flags list shows a compact Expired marker.
- The staleness badge’s tooltip lists
PastExpiryalongside any other reason already flagging that flag. - On Team and above, a cleanup notice email goes out at the next daily run, to the flag’s owner on Pro and above and to your organization’s Owners and Admins on Team. Free sends no email, and the Expired marker is how you find out.
Detection runs on the same hourly cycle as the rest of staleness analysis, so a newly passed date can take up to an hour to show up as stale. Extending the date into the future or removing it clears the stale marking right away, without waiting for the next cycle, unless the project’s default lifetime has also run out for that flag. A change to the default itself waits for the next hourly run.
Every expiry change is recorded in the flag’s audit log like any other field edit.
How this fits with cleanup
Section titled “How this fits with cleanup”PastExpiry is one signal among several that stale flag detection tracks, and it behaves a little differently from the traffic-based ones. The other four signals need to hold steady for 30 or 90 days before they mark a flag Stale or Dead. PastExpiry applies as soon as the date passes, with no waiting period, because a stated date is unambiguous in a way that traffic patterns aren’t.
A passed expiry date alone never opens a pull request. The flag cleanup Action works from traffic-based removal candidates, and a flag whose only stale reason is PastExpiry doesn’t qualify until an independent traffic signal agrees. That’s by design: an expiry date records intent, not proof the flag has stopped mattering, so a mistyped date should never make a flag look safe to delete.
Next steps
Section titled “Next steps”- Stale Flag Detection: the full detection system, and how
PastExpiryfits into it - How to Create Feature Flags: create the flags you’ll set expiry dates on
- Cleanup Notices: the email that goes out once the date passes
- Flag Owners: name who that email goes to
- Permanent Flags: the opposite marker, for flags that are meant to stay
- Scheduled Changes: actually turn a flag on or off at a future time
- Flag Cleanup Action: remove dead flag code by pull request
- API Reference: the full expiry endpoint contract