AI DevelopmentSecurity

OpenAI API Keys in 2026: Expiry, Key Types and Workload Identity

October 14, 2026
10 min read
Hourglass with the sand almost run out beside a closed steel padlock on slate
Share:

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)

2026-05-26Workload identity federation released: trusted workloads exchange external identity tokens for short-lived access tokens, without storing long-lived API keys.
2026-07-29Official Terraform provider released. Its guide lists projects, users, groups, roles, service accounts, certificates, rate limits and spend alerts among the managed resources.
2026-08-04Usage and Costs dashboards, the Usage API and the Costs API can filter and group by API key.
2026-08-29Mutual TLS and X.509 workload identity federation announced as generally available for the API.
2026-09-10Project API keys can get an expiration date at creation; admins can enforce a maximum key lifetime at organization or project level.
2026-09-15Organization and project controls over which kinds of API key can be created.
2026-10-06Usage tiers reduced from five to three: Build, Launch and Grow.

Usage tiers since 2026-10-06 (rate-limits guide)

FreeAllowed geography required · $100 / month usage limit
Build$5 in total credit purchases · $500 / month usage limit
Launch$100 in total credit purchases · $5,000 / month usage limit
Grow$500 in total credit purchases · $200,000 / month usage limit

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 keys
🗝️

You 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 keys
🔁

Workload 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 overview
🛂

mTLS 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 guide

Before 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. 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. 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. 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

  1. 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.
  2. Maximum lifetime: set it at organization or project level. The docs give no number or default, so choose one your rotation process can honor.
  3. 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.
  4. 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

  1. 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.
  2. 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.
  3. 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

Diego Rodriguez

Diego Rodriguez

Senior Full-Stack & AI Engineer

Diego has 10+ years of experience building production-grade AI-powered applications, from LLM orchestration and RAG pipelines to ML-driven risk detection and algorithmic trading systems.

Learn more about Diego →