OpenAI

EKM (External Keys) in the Management API

Learn how to manage external keys for EKM using the Management API.

Updated: 7 hours ago

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.write to create or delete external keys, and api.external_keys.read to list, retrieve, or validate external keys. A single key needs both scopes only if it must perform both read and write operations.

Usage

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.

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
}

Was this article helpful?