Summary
Access
EKM endpoints are accessible in the Management API via an Admin API Key. https://platform.openai.com/settings/organization/admin-keys (do not use a normal API key). Admin API keys are available to organization owners.
Use
api.external_keys.writeto create or delete external keys, andapi.external_keys.readto list, retrieve, or validate external keys. A single key needs both scopes only if it must perform both read and write operations.
Usage
Your EKM config is registered at the organization level via a new endpoint https://api.openai.com/v1/organization/external_keys.
Registering your EKM config returns an external_key_id in the form extkey_xxxx.
EKM configs are activated at the project level by passing in an external_key_id in the body of the existing Create Project https://api.openai.com/v1/organization/projects endpoint.
Restrictions
You must first test EKM on a new project via the Management API.
We recommend spinning up new projects for your EKM workloads. Please note the following best practices before you roll out EKM to your existing production projects.
Test all API features that you use in production in your test EKM API project first.
Apply a gradual rollout instead of populating EKM on all production API projects at once.
Organization-level endpoints
Register an external key on your organization
AWS
Sample request:
type: string - always “aws”
name: string - A friendly name for your config
role_arn: string - The Role ARN that OpenAI will assume in your cloud
kms_arn: string - The Key Management System ARN for the master key you manage
external_id: string - Your organization id or API project id
curl -X POST \
-H "Content-type: application/json" \
-H "Authorization: Bearer $TOKEN" \
"https://api.openai.com/v1/organization/external_keys" \
-d '{
"type": "aws",
"name": "AWS EKM Config",
"role_arn": "arn:aws:iam::<12_DIGIT_ACCOUNT_NUMBER>:role/<ROLE>",
"kms_arn": "arn:aws:kms:<REGION>:<ACCOUNT_NUMBER>:key/<UUID>",
"external_id": "<your org id or project id>"
}'Sample response:
{
"id": "extkey_xxxx",
"object": "organization.external_key",
"created_at": 1746175499,
"api_project_ids": [],
"type": "aws",
"name": "AWS EKM Config",
"role_arn": "arn:aws:iam::<ACCOUNT_NUMBER>:role/<ROLE>",
"kms_arn": "arn:aws:kms:<REGION>:<ACCOUNT_NUMBER>:key/<UUID>",
"external_id": "<your org id or project id>"
}GCP
Sample request:
type: string - always "gcp",
name: string - A friendly name for your config
workload_identity_project_number: string - The numeric GCP project number where you registered OpenAI's workload identity
workload_identity_pool_id: string - The pool containing the Workload Identity provider that you registered for OpenAI
workload_identity_provider_id: string - The Workload Identity provider that you registered for OpenAI
audience: string - The audience that OpenAI should pass in the token when we assume a role through your GCP STS
kms_project_id: string - The name of the GCP project where your KMS lives
kms_key_ring_name: string - The Key Management System key ring containing the master key you manage
kms_key_name: string - The name of the Key Management System master key
kms_key_location: string - The region where your Key Management System master key is located
If your KMS lives in a different GCP project from the one where you registered OpenAI's Workload Identity, make sure that the project containing OpenAI's Workload Identity at least has KMS enabled by going to https://console.developers.google.com/apis/api/cloudkms.googleapis.com/overview.
curl -X POST \
-H "Content-type: application/json" \
-H "Authorization: Bearer $TOKEN" \
"https://api.openai.com/v1/organization/external_keys" \
-d '{
"type": "gcp",
"name": "GCP EKM Config",
"workload_identity_project_number": "123456789012",
"workload_identity_pool_id": "openai-azure",
"workload_identity_provider_id": "openai-ekm-service-role",
"audience": "<your org id or project id>",
"kms_project_id": "adjective-noun-12345",
"kms_key_name": "openai-kms-key",
"kms_key_ring_name": "openai-kms-key-ring",
"kms_key_location": "us-east1"
}'Sample response:
{
"id": "extkey_xxxxxx",
"object": "organization.external_key",
"created_at": 1746174349,
"api_project_ids": [],
"type": "gcp",
"name": "GCP EKM Config",
"workload_identity_project_number": "123456789012",
"kms_key_ring_name": "openai-kms-key-ring",
"kms_key_name": "openai-kms-key",
"kms_key_location": "us-east1",
"audience": "<your org id or project id>",
"kms_project_id": "adjective-noun-12345",
"workload_identity_pool_id": "openai-azure",
"workload_identity_provider_id": "openai-ekm-service-role"
}Azure
Sample request:
type: string - always "azure",
name: string - A friendly name for your config
tenant_id: string - Your Azure tenant UUID
vault_uri: string - The URI of the Azure vault containing the master key you manage
key_name: string - The name of the Azure Key Vault master key that you manage.
It must have the form <org-xxx>--<any_name>
where org-xxx is your OpenAI organization ID that you can find at https://platform.openai.com/settings/organization/general
curl -X POST \
-H "Content-type: application/json" \
-H "Authorization: Bearer $TOKEN" \
"https://api.openai.com/v1/organization/external_keys" \
-d '{
"type": "azure",
"name": "Azure EKM Config",
"tenant_id": "<UUID>",
"vault_uri": "https://<VAULT_NAME>.vault.azure.net/",
"key_name": "org-xxx--some-key"
}'Sample response:
{
"id": "extkey_xxxx",
"object": "organization.external_key",
"created_at": 1746174377,
"api_project_ids": [],
"type": "azure",
"name": "Azure EKM Config",
"tenant_id": "<UUID>",
"vault_uri": "https://<VAULT_NAME>.vault.azure.net/",
"key_name": "org-xxx--some-key"
}OCI
Set up an enabled AES-256 key and grant OpenAI access. The key must have a free-form openai_organization_id tag set to your OpenAI organization ID. For setup steps, see: Configuring OCI Vault for Enterprise Key Management.
Sample request:
type: string - always "oci"
purpose: string - "ekm"; the default when omitted
name: string - A friendly name for your config
region: string - The OCI region containing your vault and key
vault_ocid: string - The full OCID of your vault
key_ocid: string - The full OCID of your AES-256 key
Do not pass key_version_ocid for EKM.
curl -X POST \
-H "Content-type: application/json" \
-H "Authorization: Bearer $TOKEN" \
"https://api.openai.com/v1/organization/external_keys" \
-d '{
"type": "oci",
"purpose": "ekm",
"name": "OCI EKM Config",
"region": "us-ashburn-1",
"vault_ocid": "<VAULT_OCID>",
"key_ocid": "<AES_KEY_OCID>"
}'Sample response:
{
"id": "extkey_xxxx",
"object": "organization.external_key",
"created_at": 1746175499,
"api_project_ids": [],
"type": "oci",
"purpose": "ekm",
"name": "OCI EKM Config",
"region": "us-ashburn-1",
"vault_ocid": "<VAULT_OCID>",
"key_ocid": "<AES_KEY_OCID>"
}The API returns HTTP 200 on success. Save the returned id and use it as external_key_id when creating a project. The existing list, retrieve, validate, and delete operations also apply to OCI registrations.
For PSP recovery, use a separate RSA-3072 registration with purpose: "psp_decryption" and an exact key_version_ocid. Set psp_decryption_key_id to its OpenAI registration ID. OpenAI needs only read access to metadata and public keys for that RSA key. For the complete setup, see: Set up asymmetric keys — OCI Vault.
Delete an external key registered on your organization
Note: Deletion is blocked while an active project or workspace uses the registration. For EKM, archive associated API projects first; a workspace-associated EKM registration remains protected. For PSP recovery, replace or clear psp_decryption_key_id on every active project that references the registration before deleting it. Deleting an OpenAI registration does not delete the cloud key.
Sample request
curl -X DELETE \
-H "Authorization: Bearer $TOKEN" \
"https://api.openai.com/v1/organization/external_keys/extkey_xxxx"Sample response
{
"id": "extkey_xxxxx",
"object": "organization.external_key.deleted",
"created_at": 1746127808,
"api_project_ids": [],
"type": "aws",
"account_number": "123456789012",
"kms_arn": "arn:aws:kms:<REGION>:<ACCOUNT_NUMBER>:key/<UUID>",
"name": "AWS EKM Config",
"role_arn": "arn:aws:iam::<ACCOUNT_NUMBER>:role/<ROLE>"
}Get the external keys registered on your organization
Without a purpose filter, the list includes both EKM and PSP recovery registrations. Use ?purpose=ekm or ?purpose=psp_decryption to filter by purpose.
Sample request
curl -X GET \
-H "Authorization: Bearer $TOKEN" \
"https://api.openai.com/v1/organization/external_keys"Sample response
{
"object": "list",
"data": [
{
"id": "extkey_xxxx",
"object": "organization.external_key",
"created_at": 1746127808,
"api_project_ids": [],
"type": "aws",
"name": "AWS EKM Config",
"account_number": "123456789012",
"kms_arn": "arn:aws:kms:<REGION>:<ACCOUNT_NUMBER>:key/<UUID>",
"role_arn": "arn:aws:iam::<ACCOUNT_NUMBER>:role/<ROLE>"
}
],
"first_id": "extkey_xxxx",
"has_more": false,
"last_id": "extkey_xxxx"
}Validate an external key
Use validation to check that OpenAI can still access your cloud key.
For EKM, OpenAI checks that it can encrypt and decrypt with the key. For revocation and caching behavior, see: key lifecycle guidance.
For PSP recovery, OpenAI retrieves the registered version’s public key and compares it with the stored snapshot. Successful validation does not confirm that your recovery identity can decrypt. Failed validation does not remove or replace the stored public-key snapshot.
Retrieving a registration with GET returns stored configuration and does not recheck access to your cloud key.
Sample request
curl -X POST -H "Authorization: Bearer $TOKEN" \
"https://api.openai.com/v1/organization/external_keys/extkey_xxx/validate"Sample response
{
"status": "success"
}Or, an error surfaced from the cloud provider.
Project-level endpoints
Create a new project with an external key ID
This is the same as the existing Create Project endpoint, with the addition of the external_key_id parameter in the request and response.
Sample request
curl -X POST \
-H "Content-type: application/json" \
-H "Authorization: Bearer $TOKEN" \
"https://api.openai.com/v1/organization/projects" \
-d '{
"name": "Some Project",
"external_key_id": "extkey_xxxx"
}'Sample response
{
"object": "organization.project",
"id": "proj_xxxxx",
"name": "Some Project",
"external_key_id": "extkey_xxxx",
"created_at": 1740012721,
"archived_at": null,
"status": "active"
}Update an existing project with an external key ID
This is the same as the existing Update Project endpoint, with the addition of the external_key_id parameter in the request and response.
We recommend spinning up new API projects for your EKM workloads. Please note the following best practices before you roll out EKM to your existing production projects.
Test all API features that you use in production in your test EKM API project first.
Apply a gradual rollout instead of populating EKM on all production API projects at once.
Sample request
curl -X POST \
-H "Content-type: application/json" \
-H "Authorization: Bearer $TOKEN" \
"https://api.openai.com/v1/organization/projects/proj_xxx" \
-d '{
"external_key_id": "extkey_xxxx"
}'List all the projects on your organization
This is the same as the existing endpoint but with the addition of external_key_id in the API response.
Sample request
curl -X GET \
-H "Authorization: Bearer $TOKEN" \
"https://api.openai.com/v1/organization/projects"Sample response
{
"object": "list",
"data": [
{
"object": "organization.project",
"id": "proj_xxxx",
"name": "Project Name",
"external_key_id": "extkey_xxxx",
"created_at": 1717798982,
"archived_at": null,
"status": "active"
}
],
"first_id": "proj_xxxx",
"last_id": "proj_xxxx",
"has_more": true
}