Since 2026-09-10, OpenAI lets you set an expiration date on project API keys and lets admins cap how long new keys may live; since 2026-09-15, admins can also restrict which kinds of key can be created. Workload identity federation (released 2026-05-26, with mTLS and X.509 generally available since 2026-08-29) goes further and lets a workload run without a stored OpenAI key at all.
These are guardrails for new credentials, not a cleanup of the ones you already have. Here is what the docs say, a before and after for a CI job and a server, what to check this week, and what they leave unspecified.
1. Context: Seven Changes Since May, as Documented
Everything below comes from OpenAI’s changelog and developer guides, which I re-read on 2026-10-14. I have not configured these features myself, so read it as what the docs state, with the date of each change, not as a field report.
Timeline (OpenAI changelog)
Usage tiers since 2026-10-06 (rate-limits guide)
The guide says an organization’s tier upgrades automatically as its total credit purchases reach each threshold. Spend alerts and hard spend limits are separate controls on the same page: a hard limit makes affected requests return a 429.
These controls only touch new keys
The production guide is explicit: the governance controls “apply only to new key creation; existing API keys are unaffected.” The key already sitting in a .env file, a CI secret or a password manager keeps working exactly as before until you replace it.
Practical consequence: the platform gives you the brakes, but the inventory and the replacement are yours. Why tiers belong in a key-security post: the tier table lists a monthly usage limit, and my reading is that it is the outer bound of what a stolen key could spend in a month if you set nothing lower. The guide does not frame it that way, so treat that as my inference.
2. Four Implications for How You Hold Credentials
Each card below is a control as the docs describe it, with the date and the guide it comes from.
Keys can expire, and admins can cap how long
2026-09-10 · API keys
The production guide strongly recommends an expiration date on every project API key plus a regular rotation process. Admins can enforce a maximum lifetime at organization or project level, so new keys must expire within the limit; a project limit cannot exceed the organization’s.
Production best practices — API keysYou can restrict which kinds of key exist
2026-09-15 · API Key Governance
In Platform settings, admins can allow only service-account keys, allow only user-owned project keys, or disable new key creation. Organization restrictions always take precedence; a project can add restrictions but not loosen them.
Production best practices — API keysWorkload identity removes the stored secret
Federation 2026-05-26 · X.509 2026-08-29
A workload presents an identity it already has (a CI token, a cloud token, a client certificate). OpenAI verifies it and exchanges it for a short-lived access token mapped to a service account, valid for at most one hour per the exchange reference. No refresh token is returned: you repeat the exchange. Documented providers include GitHub Actions, Kubernetes, AWS, Azure, Google Cloud, Oracle Cloud, SPIFFE JWT-SVIDs, generic OIDC and, for the API only, X.509.
Workload identity federation overviewmTLS is an extra gate, not a replacement
2026-08-29 · Mutual TLS
Once a trusted certificate is active for an organization or project, requests in that scope must present an accepted client certificate in addition to the normal bearer credential. The guide lists up to 50 certificate objects per organization, no CRL or OCSP checks and no Private Link compatibility, and says to activate on a non-critical project first.
Mutual TLS guideBefore and after: a CI job (GitHub Actions)
Illustrative: composed from the GitHub Actions guide’s variable names and the token exchange reference’s fields. I have not run it against a real project, and the “before” is the common pattern, not something the docs show. The guide’s own SDK examples do the exchange for you; the last block just spells out what happens.
Before: a long-lived key stored as a repository secret
jobs:
call-openai:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: node ./scripts/call-openai.js
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}After: the job proves who it is (no OpenAI secret stored)
permissions:
id-token: write
contents: read
jobs:
call-openai:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- run: node ./scripts/call-openai.js
env:
OPENAI_WIF_AUDIENCE: ${{ vars.OPENAI_WIF_AUDIENCE }}
OPENAI_IDENTITY_PROVIDER_ID: ${{ vars.OPENAI_IDENTITY_PROVIDER_ID }}
OPENAI_SERVICE_ACCOUNT_ID: ${{ vars.OPENAI_SERVICE_ACCOUNT_ID }}What the exchange looks like on the wire (needs curl and jq)
AUD=$(jq -rn --arg audience "$OPENAI_WIF_AUDIENCE" '$audience | @uri')
JWT=$(curl -s -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=$AUD" | jq -r .value)
curl -s https://auth.openai.com/oauth/token \
-H "Content-Type: application/json" \
-d "{
\"grant_type\": \"urn:ietf:params:oauth:grant-type:token-exchange\",
\"subject_token_type\": \"urn:ietf:params:oauth:token-type:jwt\",
\"subject_token\": \"$JWT\",
\"identity_provider_id\": \"$OPENAI_IDENTITY_PROVIDER_ID\",
\"service_account_id\": \"$OPENAI_SERVICE_ACCOUNT_ID\"
}"
# response: access_token, expires_in, expires_at (no refresh token)On the OpenAI side, the guide has you create a workload identity provider (issuer https://token.actions.githubusercontent.com, audience matching exactly what the workflow requests) and a service account mapping, with assertions such as repository, ref, environment and workflow_ref.
Before and after: a server workload (X.509)
Illustrative again, composed from the X.509 and Mutual TLS guides. The environment variable names are the guides’ own; the model id is a placeholder because the guides’ samples differ from page to page.
Before: a static key in an environment variable
export OPENAI_API_KEY="<static key from your secret manager>"
curl https://api.openai.com/v1/responses \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "<model>", "input": "ping"}'After: client certificate in, short-lived token out
export OPENAI_MTLS_CERT_CHAIN="/path/to/client-chain.pem"
export OPENAI_MTLS_KEY="/path/to/client-key.pem"
export OPENAI_IDENTITY_PROVIDER_ID="<provider id>"
export OPENAI_SERVICE_ACCOUNT_ID="<service account id>"
# 1. certificate in, short-lived bearer token out (keep access_token as OPENAI_WIF_ACCESS_TOKEN)
curl https://mtls.auth.openai.com/oauth/token \
--cert "$OPENAI_MTLS_CERT_CHAIN" --key "$OPENAI_MTLS_KEY" \
-H "Content-Type: application/json" \
-d "{
\"grant_type\": \"urn:ietf:params:oauth:grant-type:token-exchange\",
\"subject_token_type\": \"urn:openai:params:oauth:token-type:x509\",
\"identity_provider_id\": \"$OPENAI_IDENTITY_PROVIDER_ID\",
\"service_account_id\": \"$OPENAI_SERVICE_ACCOUNT_ID\"
}"
# 2. bearer token AND client certificate on every API call
curl https://mtls.api.openai.com/v1/responses \
--cert "$OPENAI_MTLS_CERT_CHAIN" --key "$OPENAI_MTLS_KEY" \
-H "Authorization: Bearer $OPENAI_WIF_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"model": "<model>", "input": "ping"}'Short-lived is not the same as revocable
For X.509 the guide states that the bearer token is not bound to the certificate (no DPoP, no cnf claim), that the flow does no CRL or OCSP checks, and that tokens last at most one hour and never outlive the client certificate. Its own advice is to plan incident response around Mutual TLS roots, providers, mappings and short token lifetimes.
3. What to Do This Week
Settings first, migration second, and one workload at a time. This is the sequence I would run with any team that holds OpenAI keys:
☀️ Days 1–2: Inventory and rank
- 1. List every API key per project with its owner and workload. Usage and Costs can be filtered and grouped by API key since 2026-08-04, which helps you tell live keys from dead ones.
- 2. Check usage tracking: the docs say keys created before 2023-12-20 do not have it enabled by default and earlier traffic shows as Untracked; you can turn it on for future usage from the key dashboard.
- 3. Find where each key physically lives (repo secrets, CI variables, .env files, secret managers, laptops, third-party tools). Every copy is a place it can leak from.
📅 Days 3–4: Lock the settings
- Key governance: in Platform settings, API Key Governance, pick service-account keys only, user-owned keys only or no new keys. The organization overrides projects, so set it first.
- Maximum lifetime: set it at organization or project level. The docs give no number or default, so choose one your rotation process can honor.
- Expiry and rotation: give every new project key an expiration date and follow the documented order: create the replacement, update your apps, confirm it works, then revoke the old key.
- Spend: check which tier your organization sits in after 2026-10-06 and set spend alerts plus a hard limit below the tier’s monthly usage limit.
📆 Day 5: Move one workload to federation
- CI first: under Organization Settings > Security > Workload Identity Provider, create the GitHub Actions provider and a mapping scoped by repository, ref, environment and workflow_ref. Use separate service accounts for CI and production, and never hand production credentials to untrusted fork pull requests.
- Servers: under Mutual TLS, upload your PEM trust anchor and activate it on a non-critical project; then create an X.509 provider and map an exact openai.subject to one service account with only the permissions it needs.
- Close the loop: only after the new path works, revoke the static key and delete the secret. Keep one admin key for Admin API calls, which federated tokens cannot make.
4. What the Docs Do Not Specify
A note on status first: the changelog calls Mutual TLS and X.509 federation “generally available” on 2026-08-29, while the X.509 guide itself says only that it is “available for the OpenAI API” and that Codex does not support it. These are the gaps I found in the API keys section and the federation guides:
No numbers
No maximum lifetime, default expiry or minimum is stated. “Maximum key lifetime” is a setting you choose, and the docs do not recommend a value.
Key types are undefined
“Service-account keys” and “user-owned project keys” are not defined in that section, and it does not say whether the lifetime cap covers both. Test with a throwaway project before you rely on it.
No leaked-key runbook
Revocation appears only as the last step of planned rotation. There is no incident procedure for a key that has already leaked, so write your own: revoke, review usage by key, rotate what depended on it.
Expiry behavior
Not stated: whether the expiry of an existing key can be changed, and what an expired key returns. Alert on any authentication failure instead of assuming a specific error code.
Two gotchas worth a calendar entry
Workload identity tokens cannot call Admin API endpoints, so an admin key stays in your estate and becomes your most sensitive static secret. And exactly one enabled mapping must match each exchange, with permissions not combined across mappings, so overlapping mappings make the exchange fail instead of widening access.
Golden rule for 2026
If a person has to paste it, it will live too long. Let workloads prove who they are, cap the lifetime of the keys you cannot avoid, and set the organization so that new keys can only be the kind you chose.
Conclusion
Since May, OpenAI has been giving you the pieces: keys that expire, an organization-wide say over which keys can exist, and a way for CI and servers to authenticate without holding a secret. None of it retroactively fixes the keys you already minted.
Do the one-week sprint above and you come out with an inventory, tighter defaults for every future key and one workload that no longer stores a secret. The same week OpenAI retires old models, it is a natural moment to touch every key anyway.
This week in one screen
Expire
- • Maximum key lifetime
- • Expiry on every new key
- • Documented rotation order
Restrict
- • API Key Governance
- • Service-account keys only
- • Spend alerts + hard limit
Federate
- • CI via GitHub OIDC
- • Servers via X.509
- • Revoke the static key
Sources
- OpenAI — API changelog (key expiry, key governance, mTLS and X.509, usage tiers)
- OpenAI — Production best practices: API keys, expiration and governance
- OpenAI — Workload identity federation overview
- OpenAI — Workload identity federation for GitHub Actions
- OpenAI — Workload identity federation with X.509 certificates
- OpenAI — Mutual TLS guide
- OpenAI — Workload identity token exchange reference
- OpenAI — Rate limits and usage tiers



