# OpenAI Administration API — atlas
Status: DOCUMENTED for all 124 operations (OpenAPI spec v2.3.0); every GET we could probe is ACCOUNT_RESTRICTED (our project key gets HTTP 403 — 401 for audit logs — Missing scopes: api.management.read etc.). No Admin key is available, so nothing below is LIVE_VERIFIED except the exact error shapes. No mutating admin call was ever issued.
Sources: Administration overview · Admin APIs guide · Production best practices → organization · Spend limits · Mutual TLS · IP allowlist · Your data (retention / residency) · OpenAPI sources/openai/openapi/openapi-master.yaml (all /organization/* + /projects/* paths) · SDK surfaces sources/openai/openapi/{python,node}-sdk-api.md.
Last verified: 2026-09-18 (live probes: 14 admin GETs, all 401/403; details in tmp-live/openai-admin/ and reports/live-requests.jsonl).
Machine-readable twins: generated/fragments/endpoints/openai-admin.json (124 endpoints), generated/fragments/parameters/openai-admin.json (431 parameters), generated/fragments/audit-log-events/openai-audit-log-events.json (149 event types).
# 1. Model of the Admin API
| Concept |
What it is |
Where |
| Organization |
Billing + policy boundary; has users (owner/reader), groups, custom roles, spend limit, data retention, certificates, external storage, audit log |
/v1/organization/* |
| Project |
Isolation unit inside an org: own API keys, service accounts, users (owner/member), groups, per-model rate limits, model/hosted-tool permissions, spend limit & alerts, data retention, certificates, webhooks |
/v1/organization/projects/{project_id}/*, project roles at /v1/projects/{project_id}/* |
| Admin API key |
sk-admin-… created by org owners at Settings → Organization → Admin keys; the only credential accepted by /v1/organization/* and /v1/projects/*; refused by non-admin endpoints. Workload-identity tokens cannot call Admin endpoints. |
GET/POST/DELETE /v1/organization/admin_api_keys |
| Scopes |
Fine-grained permissions carried by restricted keys / roles. Read scopes we observed in error messages: api.management.read (users, invites, projects, admin keys, spend limit/alerts, data retention), api.usage.read (usage + costs), api.audit_logs.read, api.roles.read, api.groups.read, api.mtls.read (certificates, message adds "(Owner)"), api.external_storage.read. Documented write scopes: api.groups.write, api.mtls.write. Model-side scopes seen in the spec/guides: api.model.request, api.model.read, api.responses.write, api.files.read/write, api.batch.read/write, api.vector_store.read, api.videos.read/write, api.voices.read/write, api.agents.read/write, api.vaults.read/write, api.webhooks.read, api.traces.read, api.safety.read, api.safety.alerts.read, api.apps.read/write. |
error bodies, Role.permissions[] |
| Roles |
Predefined + custom roles (object: role, permissions[], `resource_type: api.organization |
api.project, predefined_role). Bound to users or groups at org level (/organization/users/{id}/roles, /organization/groups/{id}/roles) or project level (/projects/{id}/users/{uid}/roles, /projects/{id}/groups/{gid}/roles). Legacy simple roles: org owner/reader; project owner/member`. |
| Pagination |
Cursor style: limit (1–100, default 20) + after → {object:"list", data, first_id, last_id, has_more}. Usage/costs use page → {object:"page", data:[buckets], has_more, next_page}. Roles/groups lists use after/limit and return has_more + next cursor (SDK SyncNextCursorPage). |
— |
| Mutations |
Always POST (no PUT/PATCH), deletes are DELETE → {object:"…deleted", id, deleted:true}. No Idempotency-Key header. |
— |
# 2. Endpoint catalogue
Legend: * = required parameter; SDK column = Python method (Node is the camelCase twin, e.g. client.admin.organization.auditLogs.list).
# Organization admin api keys
| Method |
Path |
Name |
Key params (query/body) |
Python SDK |
Status |
GET |
/v1/organization/admin_api_keys |
List all organization and project API keys. |
after, order (asc |
desc), limit |
client.admin.organization.admin_api_keys.list |
POST |
/v1/organization/admin_api_keys |
Create admin API key |
name*, expires_in_seconds |
client.admin.organization.admin_api_keys.create |
DOCUMENTED |
GET |
/v1/organization/admin_api_keys/{key_id} |
Retrieve admin API key |
— |
client.admin.organization.admin_api_keys.retrieve |
DOCUMENTED |
DELETE |
/v1/organization/admin_api_keys/{key_id} |
Delete admin API key |
— |
client.admin.organization.admin_api_keys.delete |
DOCUMENTED |
# Organization audit logs
| Method |
Path |
Name |
Key params (query/body) |
Python SDK |
Status |
GET |
/v1/organization/audit_logs |
List audit logs |
effective_at, tenant_only, limit, after, before |
client.admin.organization.audit_logs.list |
ACCOUNT_RESTRICTED (403/401 with project key) |
# Certificates (Mutual TLS)
| Method |
Path |
Name |
Key params (query/body) |
Python SDK |
Status |
GET |
/v1/organization/certificates |
List organization certificates |
limit, after, order (asc |
desc) |
client.admin.organization.certificates.list |
POST |
/v1/organization/certificates |
Upload certificate |
name, certificate* |
client.admin.organization.certificates.create |
DOCUMENTED |
POST |
/v1/organization/certificates/activate |
Activate certificates for organization |
certificate_ids* |
client.admin.organization.certificates.activate |
DOCUMENTED |
POST |
/v1/organization/certificates/deactivate |
Deactivate certificates for organization |
certificate_ids* |
client.admin.organization.certificates.deactivate |
DOCUMENTED |
GET |
/v1/organization/certificates/{certificate_id} |
Get certificate |
include (content) |
client.admin.organization.certificates.retrieve |
DOCUMENTED |
POST |
/v1/organization/certificates/{certificate_id} |
Modify certificate |
name |
client.admin.organization.certificates.update |
DOCUMENTED |
DELETE |
/v1/organization/certificates/{certificate_id} |
Delete certificate |
— |
client.admin.organization.certificates.delete |
DOCUMENTED |
GET |
/v1/organization/projects/{project_id}/certificates |
List project certificates |
limit, after, order (asc |
desc) |
client.admin.organization.projects.certificates.list |
POST |
/v1/organization/projects/{project_id}/certificates/activate |
Activate certificates for project |
certificate_ids* |
client.admin.organization.projects.certificates.activate |
DOCUMENTED |
POST |
/v1/organization/projects/{project_id}/certificates/deactivate |
Deactivate certificates for project |
certificate_ids* |
client.admin.organization.projects.certificates.deactivate |
DOCUMENTED |
# Usage & costs
| Method |
Path |
Name |
Key params (query/body) |
Python SDK |
Status |
GET |
/v1/organization/costs |
Costs |
start_time*, end_time, bucket_width (1d), project_ids, api_key_ids, line_items, group_by (project_id |
line_item |
api_key_id), limit, page |
GET |
/v1/organization/usage/audio_speeches |
Audio speeches |
start_time*, end_time, bucket_width (1m |
1h |
1d), project_ids, user_ids, api_key_ids, models, group_by (project_id |
GET |
/v1/organization/usage/audio_transcriptions |
Audio transcriptions |
start_time*, end_time, bucket_width (1m |
1h |
1d), project_ids, user_ids, api_key_ids, models, group_by (project_id |
GET |
/v1/organization/usage/code_interpreter_sessions |
Code interpreter sessions |
start_time*, end_time, bucket_width (1m |
1h |
1d), project_ids, group_by (project_id), limit, page |
GET |
/v1/organization/usage/completions |
Completions |
start_time*, end_time, bucket_width (1m |
1h |
1d), project_ids, user_ids, api_key_ids, models, batch, group_by (project_id |
GET |
/v1/organization/usage/embeddings |
Embeddings |
start_time*, end_time, bucket_width (1m |
1h |
1d), project_ids, user_ids, api_key_ids, models, group_by (project_id |
GET |
/v1/organization/usage/file_search_calls |
File search calls |
start_time*, end_time, bucket_width (1m |
1h |
1d), project_ids, user_ids, api_key_ids, vector_store_ids, group_by (project_id |
GET |
/v1/organization/usage/images |
Images |
start_time*, end_time, bucket_width (1m |
1h |
1d), sources (image.generation |
GET |
/v1/organization/usage/moderations |
Moderations |
start_time*, end_time, bucket_width (1m |
1h |
1d), project_ids, user_ids, api_key_ids, models, group_by (project_id |
GET |
/v1/organization/usage/vector_stores |
Vector stores |
start_time*, end_time, bucket_width (1m |
1h |
1d), project_ids, group_by (project_id), limit, page |
GET |
/v1/organization/usage/web_search_calls |
Web search calls |
start_time*, end_time, bucket_width (1m |
1h |
1d), project_ids, user_ids, api_key_ids, models, context_levels (low |
# Organization data retention
| Method |
Path |
Name |
Key params (query/body) |
Python SDK |
Status |
GET |
/v1/organization/data_retention |
Retrieve organization data retention |
— |
client.admin.organization.data_retention.retrieve |
ACCOUNT_RESTRICTED (403/401 with project key) |
POST |
/v1/organization/data_retention |
Update organization data retention |
retention_type* (zero_data_retention |
modified_abuse_monitoring |
enhanced_zero_data_retention |
# Organization groups
| Method |
Path |
Name |
Key params (query/body) |
Python SDK |
Status |
GET |
/v1/organization/groups |
List groups |
limit, after, order (asc |
desc) |
client.admin.organization.groups.list |
POST |
/v1/organization/groups |
Create group |
name* |
client.admin.organization.groups.create |
DOCUMENTED |
GET |
/v1/organization/groups/{group_id} |
Retrieve group |
— |
client.admin.organization.groups.retrieve |
DOCUMENTED |
POST |
/v1/organization/groups/{group_id} |
Update group |
name* |
client.admin.organization.groups.update |
DOCUMENTED |
DELETE |
/v1/organization/groups/{group_id} |
Delete group |
— |
client.admin.organization.groups.delete |
DOCUMENTED |
# Organization role bindings
| Method |
Path |
Name |
Key params (query/body) |
Python SDK |
Status |
GET |
/v1/organization/groups/{group_id}/roles |
List group organization role assignments |
limit, after, order (asc |
desc) |
client.admin.organization.groups.roles.list |
POST |
/v1/organization/groups/{group_id}/roles |
Assign organization role to group |
role_id* |
client.admin.organization.groups.roles.create |
DOCUMENTED |
GET |
/v1/organization/groups/{group_id}/roles/{role_id} |
Retrieve group organization role |
— |
client.admin.organization.groups.roles.retrieve |
DOCUMENTED |
DELETE |
/v1/organization/groups/{group_id}/roles/{role_id} |
Unassign organization role from group |
— |
client.admin.organization.groups.roles.delete |
DOCUMENTED |
GET |
/v1/organization/users/{user_id}/roles |
List user organization role assignments |
limit, after, order (asc |
desc) |
client.admin.organization.users.roles.list |
POST |
/v1/organization/users/{user_id}/roles |
Assign organization role to user |
role_id* |
client.admin.organization.users.roles.create |
DOCUMENTED |
GET |
/v1/organization/users/{user_id}/roles/{role_id} |
Retrieve user organization role |
— |
client.admin.organization.users.roles.retrieve |
DOCUMENTED |
DELETE |
/v1/organization/users/{user_id}/roles/{role_id} |
Unassign organization role from user |
— |
client.admin.organization.users.roles.delete |
DOCUMENTED |
# Group membership
| Method |
Path |
Name |
Key params (query/body) |
Python SDK |
Status |
GET |
/v1/organization/groups/{group_id}/users |
List group users |
limit, after, order (asc |
desc) |
client.admin.organization.groups.users.list |
POST |
/v1/organization/groups/{group_id}/users |
Add group user |
user_id* |
client.admin.organization.groups.users.create |
DOCUMENTED |
GET |
/v1/organization/groups/{group_id}/users/{user_id} |
Retrieve group user |
— |
client.admin.organization.groups.users.retrieve |
DOCUMENTED |
DELETE |
/v1/organization/groups/{group_id}/users/{user_id} |
Remove group user |
— |
client.admin.organization.groups.users.delete |
DOCUMENTED |
# Organization invites
| Method |
Path |
Name |
Key params (query/body) |
Python SDK |
Status |
GET |
/v1/organization/invites |
List invites |
limit, after |
client.admin.organization.invites.list |
ACCOUNT_RESTRICTED (403/401 with project key) |
POST |
/v1/organization/invites |
Create invite |
email, role (reader |
owner), projects |
client.admin.organization.invites.create |
GET |
/v1/organization/invites/{invite_id} |
Retrieve invite |
— |
client.admin.organization.invites.retrieve |
DOCUMENTED |
DELETE |
/v1/organization/invites/{invite_id} |
Delete invite |
— |
client.admin.organization.invites.delete |
DOCUMENTED |
# Organization projects
| Method |
Path |
Name |
Key params (query/body) |
Python SDK |
Status |
GET |
/v1/organization/projects |
List projects |
limit, after, include_archived |
client.admin.organization.projects.list |
ACCOUNT_RESTRICTED (403/401 with project key) |
POST |
/v1/organization/projects |
Create project |
name*, geography, residency, external_key_id |
client.admin.organization.projects.create |
DOCUMENTED |
GET |
/v1/organization/projects/{project_id} |
Retrieve project |
— |
client.admin.organization.projects.retrieve |
DOCUMENTED |
POST |
/v1/organization/projects/{project_id} |
Modify project |
name, external_key_id, geography |
client.admin.organization.projects.update |
DOCUMENTED |
# Project api keys
| Method |
Path |
Name |
Key params (query/body) |
Python SDK |
Status |
GET |
/v1/organization/projects/{project_id}/api_keys |
List project API keys |
limit, after, owner_project_access (active |
inactive |
any) |
GET |
/v1/organization/projects/{project_id}/api_keys/{api_key_id} |
Retrieve project API key |
— |
client.admin.organization.projects.api_keys.retrieve |
DOCUMENTED |
DELETE |
/v1/organization/projects/{project_id}/api_keys/{api_key_id} |
Delete project API key |
— |
client.admin.organization.projects.api_keys.delete |
DOCUMENTED |
# Project archive
| Method |
Path |
Name |
Key params (query/body) |
Python SDK |
Status |
POST |
/v1/organization/projects/{project_id}/archive |
Archive project |
— |
client.admin.organization.projects.archive |
DOCUMENTED |
# Project data retention
| Method |
Path |
Name |
Key params (query/body) |
Python SDK |
Status |
GET |
/v1/organization/projects/{project_id}/data_retention |
Retrieve project data retention |
— |
client.admin.organization.projects.data_retention.retrieve |
DOCUMENTED |
POST |
/v1/organization/projects/{project_id}/data_retention |
Update project data retention |
retention_type* (organization_default |
none |
zero_data_retention |
# Project groups
| Method |
Path |
Name |
Key params (query/body) |
Python SDK |
Status |
GET |
/v1/organization/projects/{project_id}/groups |
List project groups |
limit, after, order (asc |
desc) |
client.admin.organization.projects.groups.list |
POST |
/v1/organization/projects/{project_id}/groups |
Add project group |
group_id, role |
client.admin.organization.projects.groups.create |
DOCUMENTED |
GET |
/v1/organization/projects/{project_id}/groups/{group_id} |
Retrieve project group |
group_type (group |
tenant_group) |
client.admin.organization.projects.groups.retrieve |
DELETE |
/v1/organization/projects/{project_id}/groups/{group_id} |
Remove project group |
— |
client.admin.organization.projects.groups.delete |
DOCUMENTED |
| Method |
Path |
Name |
Key params (query/body) |
Python SDK |
Status |
GET |
/v1/organization/projects/{project_id}/hosted_tool_permissions |
Retrieve project hosted tool permissions |
— |
client.admin.organization.projects.hosted_tool_permissions.retrieve |
DOCUMENTED |
POST |
/v1/organization/projects/{project_id}/hosted_tool_permissions |
Modify project hosted tool permissions |
file_search, web_search, image_generation, mcp, code_interpreter |
client.admin.organization.projects.hosted_tool_permissions.update |
DOCUMENTED |
# Project model permissions
| Method |
Path |
Name |
Key params (query/body) |
Python SDK |
Status |
GET |
/v1/organization/projects/{project_id}/model_permissions |
Retrieve project model permissions |
— |
client.admin.organization.projects.model_permissions.retrieve |
DOCUMENTED |
POST |
/v1/organization/projects/{project_id}/model_permissions |
Modify project model permissions |
mode* (allow_list |
deny_list), model_ids* |
client.admin.organization.projects.model_permissions.update |
DELETE |
/v1/organization/projects/{project_id}/model_permissions |
Delete project model permissions |
— |
client.admin.organization.projects.model_permissions.delete |
DOCUMENTED |
# Project rate limits
| Method |
Path |
Name |
Key params (query/body) |
Python SDK |
Status |
GET |
/v1/organization/projects/{project_id}/rate_limits |
List project rate limits |
limit, after, before |
client.admin.organization.projects.rate_limits.list_rate_limits |
DOCUMENTED |
POST |
/v1/organization/projects/{project_id}/rate_limits/{rate_limit_id} |
Modify project rate limit |
max_requests_per_1_minute, max_tokens_per_1_minute, max_images_per_1_minute, max_audio_megabytes_per_1_minute, max_requests_per_1_day, batch_1_day_max_input_tokens |
client.admin.organization.projects.rate_limits.update_rate_limit |
DOCUMENTED |
# Project service accounts
| Method |
Path |
Name |
Key params (query/body) |
Python SDK |
Status |
GET |
/v1/organization/projects/{project_id}/service_accounts |
List project service accounts |
limit, after |
client.admin.organization.projects.service_accounts.list |
DOCUMENTED |
POST |
/v1/organization/projects/{project_id}/service_accounts |
Create project service account |
name*, create_service_account_only, expires_in_seconds |
client.admin.organization.projects.service_accounts.create |
DOCUMENTED |
GET |
/v1/organization/projects/{project_id}/service_accounts/{service_account_id} |
Retrieve project service account |
— |
client.admin.organization.projects.service_accounts.retrieve |
DOCUMENTED |
POST |
/v1/organization/projects/{project_id}/service_accounts/{service_account_id} |
Update project service account |
name, role (member |
owner) |
client.admin.organization.projects.service_accounts.update |
DELETE |
/v1/organization/projects/{project_id}/service_accounts/{service_account_id} |
Delete project service account |
— |
client.admin.organization.projects.service_accounts.delete |
DOCUMENTED |
POST |
/v1/organization/projects/{project_id}/service_accounts/{service_account_id}/api_keys |
Create an API key for a service account |
name, scopes, expires_in_seconds |
client.admin.organization.projects.service_accounts.api_keys.create |
DOCUMENTED |
# Project spend alerts
| Method |
Path |
Name |
Key params (query/body) |
Python SDK |
Status |
GET |
/v1/organization/projects/{project_id}/spend_alerts |
List project spend alerts |
limit, order (asc |
desc), after, before |
client.admin.organization.projects.spend_alerts.list |
POST |
/v1/organization/projects/{project_id}/spend_alerts |
Create project spend alert |
threshold_amount, currency (USD), interval* (month), notification_channel* |
client.admin.organization.projects.spend_alerts.create |
DOCUMENTED |
GET |
/v1/organization/projects/{project_id}/spend_alerts/{alert_id} |
Retrieve project spend alert |
— |
client.admin.organization.projects.spend_alerts.retrieve |
DOCUMENTED |
POST |
/v1/organization/projects/{project_id}/spend_alerts/{alert_id} |
Update project spend alert |
threshold_amount, currency (USD), interval* (month), notification_channel* |
client.admin.organization.projects.spend_alerts.update |
DOCUMENTED |
DELETE |
/v1/organization/projects/{project_id}/spend_alerts/{alert_id} |
Delete project spend alert |
— |
client.admin.organization.projects.spend_alerts.delete |
DOCUMENTED |
# Project users
| Method |
Path |
Name |
Key params (query/body) |
Python SDK |
Status |
GET |
/v1/organization/projects/{project_id}/users |
List project users |
limit, after |
client.admin.organization.projects.users.list |
DOCUMENTED |
POST |
/v1/organization/projects/{project_id}/users |
Create project user |
user_id, email, role* |
client.admin.organization.projects.users.create |
DOCUMENTED |
GET |
/v1/organization/projects/{project_id}/users/{user_id} |
Retrieve project user |
— |
client.admin.organization.projects.users.retrieve |
DOCUMENTED |
POST |
/v1/organization/projects/{project_id}/users/{user_id} |
Modify project user |
role |
client.admin.organization.projects.users.update |
DOCUMENTED |
DELETE |
/v1/organization/projects/{project_id}/users/{user_id} |
Delete project user |
— |
client.admin.organization.projects.users.delete |
DOCUMENTED |
# Organization roles
| Method |
Path |
Name |
Key params (query/body) |
Python SDK |
Status |
GET |
/v1/organization/roles |
List organization roles |
limit, after, order (asc |
desc) |
client.admin.organization.roles.list |
POST |
/v1/organization/roles |
Create organization role |
role_name, permissions, description |
client.admin.organization.roles.create |
DOCUMENTED |
GET |
/v1/organization/roles/{role_id} |
Retrieve organization role |
— |
client.admin.organization.roles.retrieve |
DOCUMENTED |
POST |
/v1/organization/roles/{role_id} |
Update organization role |
permissions, description, role_name |
client.admin.organization.roles.update |
DOCUMENTED |
DELETE |
/v1/organization/roles/{role_id} |
Delete organization role |
— |
client.admin.organization.roles.delete |
DOCUMENTED |
# Organization spend alerts
| Method |
Path |
Name |
Key params (query/body) |
Python SDK |
Status |
GET |
/v1/organization/spend_alerts |
List organization spend alerts |
limit, order (asc |
desc), after, before |
client.admin.organization.spend_alerts.list |
POST |
/v1/organization/spend_alerts |
Create organization spend alert |
threshold_amount, currency (USD), interval* (month), notification_channel* |
client.admin.organization.spend_alerts.create |
DOCUMENTED |
GET |
/v1/organization/spend_alerts/{alert_id} |
Retrieve organization spend alert |
— |
client.admin.organization.spend_alerts.retrieve |
DOCUMENTED |
POST |
/v1/organization/spend_alerts/{alert_id} |
Update organization spend alert |
threshold_amount, currency (USD), interval* (month), notification_channel* |
client.admin.organization.spend_alerts.update |
DOCUMENTED |
DELETE |
/v1/organization/spend_alerts/{alert_id} |
Delete organization spend alert |
— |
client.admin.organization.spend_alerts.delete |
DOCUMENTED |
# Organization users
| Method |
Path |
Name |
Key params (query/body) |
Python SDK |
Status |
GET |
/v1/organization/users |
List users |
limit, after, emails |
client.admin.organization.users.list |
ACCOUNT_RESTRICTED (403/401 with project key) |
GET |
/v1/organization/users/{user_id} |
Retrieve user |
— |
client.admin.organization.users.retrieve |
DOCUMENTED |
POST |
/v1/organization/users/{user_id} |
Modify user |
role, role_id, technical_level, developer_persona |
client.admin.organization.users.update |
DOCUMENTED |
DELETE |
/v1/organization/users/{user_id} |
Delete user |
— |
client.admin.organization.users.delete |
DOCUMENTED |
# Project roles & role bindings
| Method |
Path |
Name |
Key params (query/body) |
Python SDK |
Status |
GET |
/v1/projects/{project_id}/groups/{group_id}/roles |
List project group role assignments |
limit, after, order (asc |
desc) |
client.admin.organization.projects.groups.roles.list |
POST |
/v1/projects/{project_id}/groups/{group_id}/roles |
Assign project role to group |
role_id* |
client.admin.organization.projects.groups.roles.create |
DOCUMENTED |
GET |
/v1/projects/{project_id}/groups/{group_id}/roles/{role_id} |
Retrieve project group role |
— |
client.admin.organization.projects.groups.roles.retrieve |
DOCUMENTED |
DELETE |
/v1/projects/{project_id}/groups/{group_id}/roles/{role_id} |
Unassign project role from group |
— |
client.admin.organization.projects.groups.roles.delete |
DOCUMENTED |
GET |
/v1/projects/{project_id}/roles |
List project roles |
limit, after, order (asc |
desc) |
client.admin.organization.projects.roles.list |
POST |
/v1/projects/{project_id}/roles |
Create project role |
role_name, permissions, description |
client.admin.organization.projects.roles.create |
DOCUMENTED |
GET |
/v1/projects/{project_id}/roles/{role_id} |
Retrieve project role |
— |
client.admin.organization.projects.roles.retrieve |
DOCUMENTED |
POST |
/v1/projects/{project_id}/roles/{role_id} |
Update project role |
permissions, description, role_name |
client.admin.organization.projects.roles.update |
DOCUMENTED |
DELETE |
/v1/projects/{project_id}/roles/{role_id} |
Delete project role |
— |
client.admin.organization.projects.roles.delete |
DOCUMENTED |
GET |
/v1/projects/{project_id}/users/{user_id}/roles |
List project user role assignments |
limit, after, order (asc |
desc) |
client.admin.organization.projects.users.roles.list |
POST |
/v1/projects/{project_id}/users/{user_id}/roles |
Assign project role to user |
role_id* |
client.admin.organization.projects.users.roles.create |
DOCUMENTED |
GET |
/v1/projects/{project_id}/users/{user_id}/roles/{role_id} |
Retrieve project user role |
— |
client.admin.organization.projects.users.roles.retrieve |
DOCUMENTED |
DELETE |
/v1/projects/{project_id}/users/{user_id}/roles/{role_id} |
Unassign project role from user |
— |
client.admin.organization.projects.users.roles.delete |
DOCUMENTED |
# Organization external storage
| Method |
Path |
Name |
Key params (query/body) |
Python SDK |
Status |
GET |
/v1/organization/external_storage |
List external storage configurations |
project_id, after, order (asc |
desc), limit |
— |
POST |
/v1/organization/external_storage |
Create an external storage configuration |
project_id, provider |
— |
DOCUMENTED |
GET |
/v1/organization/external_storage/{external_storage_id} |
Get an external storage configuration |
— |
— |
DOCUMENTED |
DELETE |
/v1/organization/external_storage/{external_storage_id} |
Delete an external storage configuration |
— |
— |
DOCUMENTED |
POST |
/v1/organization/external_storage/{external_storage_id}/validate |
Validate an external storage configuration |
— |
— |
DOCUMENTED |
# Organization spend limit
| Method |
Path |
Name |
Key params (query/body) |
Python SDK |
Status |
GET |
/v1/organization/spend_limit |
Get organization spend limit |
— |
client.admin.organization.spend_limit.retrieve |
ACCOUNT_RESTRICTED (403/401 with project key) |
POST |
/v1/organization/spend_limit |
Update organization spend limit |
threshold_amount, currency (USD), interval* (month) |
client.admin.organization.spend_limit.update |
DOCUMENTED |
DELETE |
/v1/organization/spend_limit |
Delete organization spend limit |
— |
client.admin.organization.spend_limit.delete |
DOCUMENTED |
# Project spend limit
| Method |
Path |
Name |
Key params (query/body) |
Python SDK |
Status |
GET |
/v1/organization/projects/{project_id}/spend_limit |
Get project spend limit |
— |
client.admin.organization.projects.spend_limit.retrieve |
DOCUMENTED |
POST |
/v1/organization/projects/{project_id}/spend_limit |
Update project spend limit |
threshold_amount, currency (USD), interval* (month) |
client.admin.organization.projects.spend_limit.update |
DOCUMENTED |
DELETE |
/v1/organization/projects/{project_id}/spend_limit |
Delete project spend limit |
— |
client.admin.organization.projects.spend_limit.delete |
DOCUMENTED |
# 3. Roles & scopes catalogue
| Level |
Predefined roles (legacy strings) |
Where they appear |
| Organization |
owner (billing, members, all reader rights), reader (make API requests, view org info, manage resources) |
Invite.role, OrganizationUser.role, POST /organization/users/{id} body role |
| Project |
owner, member |
ProjectUser.role, Invite.projects[].role, POST /organization/projects/{id}/users |
| Custom roles |
Role{id, name, description, permissions[], resource_type, predefined_role} created with POST /organization/roles (org-wide) or POST /projects/{id}/roles; assigned with the …/roles binding endpoints (role_id) |
Roles API |
| API key permission levels (dashboard) |
All, Restricted (per-resource read/write), Read-only; restricted keys carry scopes like api.model.request; audit log api_key.created.data.scopes[] records them |
dashboard + audit logs |
| Key types |
project key sk-proj-… (owned by a user or a service account, `owner.type: user |
service_account, owner_project_access: active |
Governance knobs documented in production-best-practices: max API-key lifetime (org and project, project ≤ org), API Key Governance (allow only service-account keys / only user keys / block new keys; org restrictions win), key usage tracking (keys created before 2023-12-20 are untracked by default).
# 4. Usage & costs query cookbook (ACCOUNT_RESTRICTED for us — verified 403 api.usage.read)
All usage endpoints share the same query grammar (from the OpenAPI spec):
| Param |
Type |
Notes |
start_time* |
int (unix s) |
inclusive |
end_time |
int |
exclusive |
bucket_width |
1m / 1h / 1d (default 1d) |
costs: only 1d |
limit |
int |
buckets per page: 1d ≤ 31 (default 7), 1h ≤ 168 (default 24), 1m ≤ 1440 (default 60) |
page |
string |
cursor from next_page |
project_ids, user_ids, api_key_ids, models |
string[] |
filters (repeat the query key) |
batch |
bool |
completions/embeddings/…: true = Batch API traffic only |
group_by |
string[] |
completions: project_id, user_id, api_key_id, model, batch, service_tier; costs: project_id, line_item; images add size, source; audio speeches/transcriptions add model; code_interpreter_sessions/vector_stores: project_id |
| Endpoint |
Result object |
Key metrics |
GET /v1/organization/usage/completions |
organization.usage.completions.result |
`input_tokens, input_cached_tokens, input_cache_write_tokens, input_uncached_tokens, output_tokens, input/output_text |
…/usage/embeddings |
organization.usage.embeddings.result |
input_tokens, num_model_requests |
…/usage/moderations |
organization.usage.moderations.result |
input_tokens, num_model_requests |
…/usage/images |
organization.usage.images.result |
images, num_model_requests, dims size (256x256…1536x1024), source (image.generation, image.edit, image.variation) |
…/usage/audio_speeches |
organization.usage.audio_speeches.result |
characters, num_model_requests |
…/usage/audio_transcriptions |
organization.usage.audio_transcriptions.result |
seconds, num_model_requests |
…/usage/vector_stores |
organization.usage.vector_stores.result |
usage_bytes |
…/usage/code_interpreter_sessions |
organization.usage.code_interpreter_sessions.result |
num_sessions |
…/usage/file_search_calls, …/usage/web_search_calls |
organization.usage.*_calls.result |
num_calls (web search adds search_context_size) |
GET /v1/organization/costs |
organization.costs.result |
amount{value, currency:"usd"}, line_item, project_id, organization_id |
Recipes (curl; -G turns --data-urlencode into query params):
# daily completions tokens per model for the last 7 days
curl -G https://api.openai.com/v1/organization/usage/completions -H "Authorization: Bearer $OPENAI_ADMIN_KEY" \
--data-urlencode "start_time=$(( $(date +%s) - 7*86400 ))" --data-urlencode bucket_width=1d \
--data-urlencode group_by=model --data-urlencode group_by=service_tier --data-urlencode limit=7
# hourly usage of one project, Batch only, paginated
curl -G https://api.openai.com/v1/organization/usage/completions -H "Authorization: Bearer $OPENAI_ADMIN_KEY" \
--data-urlencode start_time=1789000000 --data-urlencode bucket_width=1h --data-urlencode project_ids=proj_XXX \
--data-urlencode batch=true --data-urlencode limit=168 --data-urlencode page=page_AAAA…
# spend in USD per day per line item and project
curl -G https://api.openai.com/v1/organization/costs -H "Authorization: Bearer $OPENAI_ADMIN_KEY" \
--data-urlencode start_time=$(( $(date +%s) - 30*86400 )) --data-urlencode bucket_width=1d \
--data-urlencode group_by=line_item --data-urlencode group_by=project_id --data-urlencode limit=31
Python: client.admin.organization.usage.completions(start_time=…, bucket_width="1d", group_by=["model"], limit=7); costs: client.admin.organization.usage.costs(...). Runnable examples: examples/openai/admin/usage_costs.sh, admin_readonly.py, admin_readonly.ts (all return 403 on our account).
# 5. Spend limits, alerts, data retention, permissions
| Resource |
Endpoints |
Body / semantics |
| Org spend limit |
GET/POST/DELETE /v1/organization/spend_limit |
threshold_amount (cents), currency: USD, interval: month; hitting it → 429 organization_spend_limit_exceeded on API traffic |
| Project spend limit |
GET/POST/DELETE /v1/organization/projects/{p}/spend_limit |
same body; → 429 project_spend_limit_exceeded |
| Spend alerts |
/v1/organization/spend_alerts[/{id}], /v1/organization/projects/{p}/spend_alerts[/{id}] |
threshold_amount, currency, interval, notification_channel{type:"email", recipients[], subject_prefix} |
| Data retention |
GET/POST /v1/organization/data_retention, GET/POST /v1/organization/projects/{p}/data_retention |
project retention_type: organization_default inherits; org sets the policy (ZDR / modified retention need approval) |
| Model permissions |
GET/POST/DELETE /v1/organization/projects/{p}/model_permissions |
`mode: allow_list |
| Hosted tool permissions |
GET/POST /v1/organization/projects/{p}/hosted_tool_permissions |
allow/deny hosted tools (web search, code interpreter, file search, MCP, …) per project |
| Rate limits |
GET /v1/organization/projects/{p}/rate_limits, POST …/rate_limits/{rate_limit_id} |
project.rate_limit{model, max_requests_per_1_minute, max_tokens_per_1_minute, max_images_per_1_minute, max_audio_megabytes_per_1_minute, max_requests_per_1_day, batch_1_day_max_input_tokens}; a project limit can only lower the org limit |
| Certificates (mTLS) |
org: GET/POST /v1/organization/certificates, …/{id} GET/POST/DELETE, …/activate, …/deactivate; project: GET …/projects/{p}/certificates, …/activate, …/deactivate |
upload PEM trust anchor (content, name), activate = enforce; API mTLS host mtls.api.openai.com; scopes api.mtls.read/write |
| External storage |
GET/POST /v1/organization/external_storage, GET/DELETE …/{id}, POST …/{id}/validate |
bring-your-own bucket for stored artifacts; audit events external_storage.registered/removed |
| Archive project |
POST /v1/organization/projects/{p}/archive |
irreversible; status: archived |
# 6. Audit logs
GET /v1/organization/audit_logs (scope api.audit_logs.read; HTTP 401 not 403 without it). Filters: effective_at[gt|gte|lt|lte], project_ids[], event_types[], actor_ids[], actor_emails[], resource_ids[], limit, after, before. Each entry: {object:"organization.audit_log", id, type, effective_at, project{id,name}, actor{type: session|api_key, session{user, ip_address, user_agent, ja3, ja4, ip_address_details}, api_key{type: user|service_account, id, user|service_account}}, <type>: {payload}}.
| Category |
Event types (149 total) |
| api_key |
api_key.created, api_key.updated, api_key.deleted |
| certificate |
certificate.created, certificate.updated, certificate.deleted |
| certificates |
certificates.activated, certificates.deactivated |
| checkpoint |
checkpoint.permission.created, checkpoint.permission.deleted |
| external_key |
external_key.registered, external_key.removed |
| external_storage |
external_storage.registered, external_storage.removed |
| group |
group.created, group.updated, group.deleted |
| invite |
invite.sent, invite.accepted, invite.deleted |
| ip_allowlist |
ip_allowlist.created, ip_allowlist.updated, ip_allowlist.deleted, ip_allowlist.config.activated, ip_allowlist.config.deactivated |
| login |
login.succeeded, login.failed |
| logout |
logout.succeeded, logout.failed |
| organization |
organization.updated |
| project |
project.created, project.updated, project.archived, project.deleted |
| rate_limit |
rate_limit.updated, rate_limit.deleted |
| resource |
resource.deleted |
| tunnel |
tunnel.created, tunnel.updated, tunnel.deleted |
| workload_identity_provider |
workload_identity_provider.created, workload_identity_provider.updated, workload_identity_provider.deleted |
| workload_identity_provider_mapping |
workload_identity_provider_mapping.created, workload_identity_provider_mapping.updated, workload_identity_provider_mapping.deleted |
| role |
role.created, role.updated, role.deleted, role.assignment.created, role.assignment.deleted, role.bound_to_resource, role.unbound_from_resource |
| scim |
scim.enabled, scim.disabled |
| service_account |
service_account.created, service_account.updated, service_account.deleted |
| user |
user.added, user.updated, user.deleted |
| tenant (enterprise workspace / ChatGPT-side) |
tenant.metadata.updated, tenant.microsoft_entra_mapping.upserted, tenant.microsoft_entra_mapping.deleted, tenant.workload_identity.provider.created, tenant.workload_identity.provider.updated, tenant.workload_identity.provider.archived, tenant.workload_identity.mapping.created, tenant.workload_identity.mapping.updated, tenant.workload_identity.mapping.archived, tenant.workload_identity.binding.created, tenant.workload_identity.principal.provisioned, tenant.workload_identity.access_token.issued, tenant.admin_api_key.created, tenant.admin_api_key.updated, tenant.admin_api_key.deleted, tenant.project_api_key.created, tenant.trusted_access.business_verification.started, tenant.trusted_access.application.submitted, tenant.chatgpt_access_token.revoked, tenant.migration.completed, tenant.sso.migrated, tenant.domains.migrated, tenant.sso_connection.created, tenant.sso_connection.updated, tenant.sso_connection.deleted, tenant.sso_connection.setup.started, tenant.policy.created, tenant.policy.updated, tenant.policy.deleted, tenant.policy.attached, tenant.policy.detached, tenant.principal_authentication_policy.resolved, tenant.scim.setup.started, tenant.scim.deletion.requested, tenant.scim.directory.created, tenant.product_access_policy.updated, tenant.resource_share_grant.created, tenant.resource_share_grant.updated, tenant.resource_share_grant.accepted, tenant.resource_share_grant.declined, tenant.resource_share_grant.revoked, tenant.resource_share_grant.deleted, tenant.service_account.updated, tenant.service_account.deleted, tenant.service_account.token.revoked, tenant.billing.overage_limit.updated, tenant.billing.alerts.updated, tenant.billing.info.updated, tenant.usage_limit.workspace.updated, tenant.usage_limit.group.updated, tenant.usage_limit.user.updated, tenant.usage_limit.increase_request.updated, tenant.usage_limit.increase_request.resolved, tenant.group.created, tenant.group.updated, tenant.group.deleted, tenant.group.member.added, tenant.group.member.removed, tenant.migration_rollout.status.updated, tenant.migration_rollout.tier.updated, tenant.role.metadata.updated, tenant.custom_role.created, tenant.custom_role.updated, tenant.custom_role.deleted, tenant.role_assignment.created, tenant.role_assignment.deleted, tenant.resource_role_assignment.created, tenant.resource_role_assignment.deleted, tenant.resource_access.updated, tenant.resource_access.deleted, tenant.ads_account.onboarding.redemption, tenant.session_policy.created, tenant.session_policy.updated, tenant.session_policy.deleted, tenant.session_revocation.started, tenant.third_party_app_policy.updated, tenant.user.added, tenant.user.updated, tenant.user.removed, tenant.user.looked_up, tenant.user.invited, tenant.membership.revoked, tenant.api_organization_invite.upserted, tenant.api_organization_invite.deleted, tenant.chatgpt_workspace_invite.upserted, tenant.membership.accepted, tenant.membership.declined, tenant.workspace_invite_email_settings.updated |
Payload fields per event are in generated/fragments/audit-log-events/openai-audit-log-events.json (57 events carry a typed payload in the spec; the tenant.* enterprise-workspace events are enum-only).
# 7. Verified behaviour with a non-admin key (2026-09-18)
| Call |
HTTP |
Body |
GET /v1/organization/{users,invites,projects,admin_api_keys,spend_limit,spend_alerts,data_retention} |
403 |
{"error": "You have insufficient permissions for this operation. Missing scopes: api.management.read. Check that you have the correct role in your organization, and if you're using a restricted API key, that it has the necessary scopes."} — note error is a bare string |
GET /v1/organization/usage/completions, GET /v1/organization/costs |
403 |
same shape, api.usage.read |
GET /v1/organization/roles / groups |
403 |
api.roles.read / api.groups.read |
GET /v1/organization/certificates |
403 |
api.mtls.read … correct role in your organization (Owner) |
GET /v1/organization/external_storage |
403 |
structured: {"error":{"message":"… api.external_storage.read …","type":"invalid_request_error","param":null,"code":"insufficient_permissions"}} |
GET /v1/organization/audit_logs |
401 |
structured, api.audit_logs.read, code: null |
Response headers on these errors still include x-request-id, openai-version: 2020-10-01, openai-processing-ms, and (except certificates/audit logs) openai-organization / openai-project.
# 8. SDK access
from openai import OpenAI
client = OpenAI(admin_api_key=os.environ["OPENAI_ADMIN_KEY"]) # Python >= 2.34.0
users = client.admin.organization.users.list(limit=20) # SyncConversationCursorPage -> iterate
for u in users: print(u.id, u.email, u.role)
client.admin.organization.projects.model_permissions.update("proj_abc", mode="allow_list", model_ids=["gpt-4.1"])
const client = new OpenAI({ adminAPIKey: process.env.OPENAI_ADMIN_KEY }); // Node >= 6.36.0
for await (const log of client.admin.organization.auditLogs.list({ limit: 50 })) console.log(log.type);
Go option.WithAdminAPIKey (≥ 3.34.0), Java OpenAIOkHttpClient.builder().adminApiKey(...) (≥ 4.34.0), Ruby OpenAI::Client.new(admin_api_key:) (≥ 0.61.0).