For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to the page URL.
Primary navigation
Theme

Set up asymmetric keys

Register a cloud-managed public key for use with OpenAI

Register an RSA-3072 asymmetric key pair from your cloud provider for use with OpenAI.

Your private key stays in AWS KMS, Azure Key Vault, or Google Cloud KMS. OpenAI stores a copy of the corresponding public key at registration.

Registering a key doesn’t configure product features or storage. This guide covers keys for PSP decryption.

Before you start

  • Have your organization ID. Use the same organization for cloud trust, registration, and API access.
  • Create an organization Admin API key with external-key read/write access. Load it securely as OPENAI_ADMIN_KEY; project inference keys won’t work.
  • Have permission to create an enabled RSA-3072 key and grant OpenAI the scoped public-key read access described in this guide.
  • Restrict private-key use to authorized users and retain old key versions while records depend on them.

Set up a cloud key

Follow the steps for your provider before registering the key. Each setup uses RSA-3072 and grants OpenAI only public-key and metadata reads.

Register your key

The examples use curl and a Bash-compatible shell. Load OPENAI_ADMIN_KEY securely, then set your organization ID:

API_BASE="https://api.openai.com/v1"
ORG_ID="<OPENAI_ORG_ID>"

: "${OPENAI_ADMIN_KEY:?Load your organization admin key securely}"

KEYS_URL="$API_BASE/organization/external_keys"

Prepare the registration request

The purpose enum accepts ekm (the default) or psp_decryption. Use psp_decryption for the asymmetric keys in this guide; ekm is for Enterprise Key Management (EKM).

Save your provider’s request as registration.json. Replace the example values; JSON doesn’t expand shell variables.

AWS KMS request

{
  "type": "aws",
  "purpose": "psp_decryption",
  "name": "asymmetric-key-aws",
  "role_arn": "<CUSTOMER_ROLE_ARN>",
  "external_id": "<OPENAI_ORG_ID>",
  "kms_arn": "<CUSTOMER_KMS_KEY_ARN>"
}

Azure Key Vault request

{
  "type": "azure",
  "purpose": "psp_decryption",
  "name": "asymmetric-key-azure",
  "tenant_id": "<CUSTOMER_TENANT_ID>",
  "vault_uri": "https://<CUSTOMER_VAULT_NAME>.vault.azure.net",
  "key_name": "<OPENAI_ORG_ID>--asymmetric-key",
  "key_version": "<CUSTOMER_KEY_VERSION>"
}

Google Cloud KMS request

{
  "type": "gcp",
  "purpose": "psp_decryption",
  "name": "asymmetric-key-gcp",
  "workload_identity_project_number": "<CUSTOMER_GCP_PROJECT_NUMBER>",
  "workload_identity_pool_id": "openai-keys",
  "workload_identity_provider_id": "openai-google",
  "audience": "<OPENAI_ORG_ID>",
  "kms_project_id": "<CUSTOMER_KMS_PROJECT_ID>",
  "kms_key_ring_name": "asymmetric-keys",
  "kms_key_name": "asymmetric-key",
  "kms_key_location": "us-central1",
  "kms_key_version": "1"
}

1. Register and validate the key

curl --fail-with-body -sS "$KEYS_URL" \
  -H "Authorization: Bearer $OPENAI_ADMIN_KEY" \
  -H "OpenAI-Organization: $ORG_ID" \
  -H "Content-Type: application/json" \
  --data @registration.json

Registration validates the key before saving it. Success returns HTTP 200 with an extkey ID and your provider configuration. Example response fields:

{
  "id": "extkey_11111111111111111111111111111111",
  "object": "organization.external_key",
  "name": "asymmetric-key-gcp",
  "type": "gcp",
  "purpose": "psp_decryption",
  "api_project_ids": []
}

Save the returned registration ID:

KEY_ID="<EXTERNAL_KEY_ID>"

2. Revalidate the key (optional)

GET returns stored details. POST /validate rechecks current access and key validity against the saved public key. It doesn’t decrypt records or refresh the snapshot.

curl --fail-with-body -sS "$KEYS_URL/$KEY_ID" \
  -H "Authorization: Bearer $OPENAI_ADMIN_KEY" \
  -H "OpenAI-Organization: $ORG_ID"
curl --fail-with-body -sS -X POST "$KEYS_URL/$KEY_ID/validate" \
  -H "Authorization: Bearer $OPENAI_ADMIN_KEY" \
  -H "OpenAI-Organization: $ORG_ID"

Expected response (HTTP 200):

{ "status": "success" }

Validate again after key, permission, or network changes. Allow IAM changes to propagate before retrying.

To use your registered key for PSP decryption, select it for a project.

List keys

Use purpose=psp_decryption to list these asymmetric-key registrations. Omitting the filter returns all purposes; purpose=ekm returns EKM only.

curl --fail-with-body -sS --get "$KEYS_URL" \
  -H "Authorization: Bearer $OPENAI_ADMIN_KEY" \
  -H "OpenAI-Organization: $ORG_ID" \
  --data-urlencode "purpose=psp_decryption" \
  --data-urlencode "order=asc" \
  --data-urlencode "limit=20"

While has_more is true, request the next page with after set to last_id. Keep purpose, order, and limit unchanged.

Replace a key

  1. Create a replacement AWS key or Azure/GCP version and grant read access.
  2. Register the replacement.
  3. Select the new extkey ID on the project and verify the selection.

Registrations are fixed snapshots; cloud rotation doesn’t update them. Keep old private keys for records that depend on them. Changing the selection doesn’t migrate existing records.

Delete a key

Clear or replace the selection on every active project before deleting the registration:

curl --fail-with-body -sS -X DELETE "$KEYS_URL/$KEY_ID" \
  -H "Authorization: Bearer $OPENAI_ADMIN_KEY" \
  -H "OpenAI-Organization: $ORG_ID"

Deletion returns HTTP 200 with object: "organization.external_key.deleted". It doesn’t delete your cloud key.

Troubleshooting

Error What to check
401 or 403 Use an organization Admin API key and confirm access to external-key registration.
400 external_key_provider_error Check cloud trust, read permissions, network access, and the key’s enabled state and algorithm.
400 request-validation errors Check the purpose, concrete AWS key ARN, and exact Azure/GCP version.
404 Check the registration ID and organization. Deleted registrations and registrations from another organization can’t be used.
external_key_used_by_active_projects Clear or replace all active project selections.
Key no longer matches Register the replacement and select its new ID.

For support, share the error and x-request-id. Never share credentials.