> For the complete documentation index, see [llms.txt](https://docs.otari.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.otari.ai/api-reference/keys.md).

# Keys

## List Keys

> List the API keys in the caller's organization.\
> \
> Requires master key authentication. An unset \`\`workspace\_id\`\` lists every key\
> in that organization; naming a workspace in another one lists nothing rather\
> than refusing, so the filter reports no more than the unfiltered read does.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"KeyInfo":{"description":"Response model for key information.","properties":{"allowed_models":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Allowed Models"},"allowed_tools":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Allowed Tools"},"capture_agent_telemetry":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Capture Agent Telemetry"},"config_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Config Name"},"created_at":{"title":"Created At","type":"string"},"end_user_budget_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"End User Budget Id"},"end_user_budget_ids":{"items":{"type":"string"},"title":"End User Budget Ids","type":"array"},"exclude_from_budget":{"title":"Exclude From Budget","type":"boolean"},"expires_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expires At"},"id":{"title":"Id","type":"string"},"is_active":{"title":"Is Active","type":"boolean"},"is_service_key":{"title":"Is Service Key","type":"boolean"},"key_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key Name"},"key_prefix":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key Prefix"},"key_suffix":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key Suffix"},"last_used_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Used At"},"max_end_users":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max End Users"},"metadata":{"additionalProperties":true,"title":"Metadata","type":"object"},"operator_disabled":{"default":false,"title":"Operator Disabled","type":"boolean"},"reject_user_mismatch":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Reject User Mismatch"},"user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id"},"workspace_id":{"format":"uuid","title":"Workspace Id","type":"string"}},"required":["id","key_prefix","key_suffix","key_name","user_id","created_at","last_used_at","expires_at","is_active","allowed_models","allowed_tools","exclude_from_budget","reject_user_mismatch","capture_agent_telemetry","is_service_key","end_user_budget_id","end_user_budget_ids","workspace_id","metadata"],"title":"KeyInfo","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/keys":{"get":{"description":"List the API keys in the caller's organization.\n\nRequires master key authentication. An unset ``workspace_id`` lists every key\nin that organization; naming a workspace in another one lists nothing rather\nthan refusing, so the filter reports no more than the unfiltered read does.","operationId":"keys-list_keys","parameters":[{"in":"query","name":"skip","required":false,"schema":{"default":0,"minimum":0,"title":"Skip","type":"integer"}},{"in":"query","name":"limit","required":false,"schema":{"default":100,"maximum":1000,"minimum":1,"title":"Limit","type":"integer"}},{"description":"Only keys in this workspace.","in":"query","name":"workspace_id","required":false,"schema":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"description":"Only keys in this workspace.","title":"Workspace Id"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/KeyInfo"},"title":"Response Keys-List Keys","type":"array"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"List Keys","tags":["keys"]}}}}
```

## Create Key

> Create a new API key in the caller's organization.\
> \
> Requires master key authentication.\
> \
> If user\_id is provided, the key will be associated with that user (creates user if it doesn't exist).\
> If user\_id is not provided, the key is associated with the shared "default" user, which is created\
> on first use. Keys without an explicit owner therefore share one identity, and so share budget,\
> usage, and files.\
> \
> \`\`workspace\_id\`\` names a workspace in the caller's organization, and omitting\
> it mints into that organization's default workspace. A key resolves that\
> organization's provider credentials and bills there, so minting into another\
> organization's workspace would spend its budget on its credentials.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"CreateKeyRequest":{"description":"Request model for creating a new API key.","properties":{"allowed_models":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"description":"Model allow-list: null = any model, [] = deny all, or canonical instance:model entries (with instance:* / instance:prefix* wildcards).","title":"Allowed Models"},"allowed_tools":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"description":"Search tool allow-list for POST /api/v1/search: null = inherit the user's, [] = no tool, or search tool names ('*' for every search tool). A model wildcard never grants a tool.","title":"Allowed Tools"},"capture_agent_telemetry":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Per-key override of the deployment-wide capture_agent_telemetry setting: null (default) inherits it, true always stores this key's coding-agent telemetry, false always discards it. Covers both behavioral events (tool_result, tool_decision, user_prompt, api_error) from POST /otlp/v1/logs and outcome-metric data points (lines of code, commits, pull requests, active time) from POST /otlp/v1/metrics. Usage capture and billing are unaffected either way.","title":"Capture Agent Telemetry"},"end_user_budget_id":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Budget each end user this key creates is capped at, unless the request names another with Otari-End-User-Budget. Null leaves end users capped only by this key's own ceiling.","title":"End User Budget Id"},"end_user_budget_ids":{"anyOf":[{"items":{"type":"string"},"maxItems":100,"type":"array"},{"type":"null"}],"description":"Budgets a request may start a new end user on by naming one in Otari-End-User-Budget. Null allows end_user_budget_id alone. When both are set, end_user_budget_id must be on the list.","title":"End User Budget Ids"},"exclude_from_budget":{"default":false,"description":"When true, requests on this key are logged with cost but never reserved, reconciled into the user's spend, or gated by budget.","title":"Exclude From Budget","type":"boolean"},"expires_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"description":"Optional expiration timestamp","title":"Expires At"},"is_service_key":{"default":false,"description":"When true, a request may name an end user in its 'user' field. Each end user is created on first use, owned by this key's user, and billed to its own budget, while this key's own ceiling caps all of them together.","title":"Is Service Key","type":"boolean"},"key_name":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional name for the key","title":"Key Name"},"max_end_users":{"anyOf":[{"minimum":0,"type":"integer"},{"type":"null"}],"description":"Most live end users this key may have on the budgets it assigns (all of its owner's end users when it assigns none). A request that would create one more is refused with 403 and end_user_limit_reached; deleting end users frees room. Null is no cap.","title":"Max End Users"},"metadata":{"additionalProperties":true,"description":"Optional metadata","title":"Metadata","type":"object"},"reject_user_mismatch":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Per-key override of the deployment-wide reject_user_mismatch setting: null (default) inherits it, true always rejects a request naming a different 'user', false always accepts it. Spend binds to this key's own user either way.","title":"Reject User Mismatch"},"user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional user ID to associate with this key","title":"User Id"},"workspace_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"description":"Workspace this key belongs to, which must be one in the caller's organization. Omitted means that organization's default workspace. A key belongs to exactly one workspace: requests on it are scoped and billed there, so the workspace is read off the key rather than off a request header.","title":"Workspace Id"}},"title":"CreateKeyRequest","type":"object"},"CreateKeyResponse":{"description":"Response model for creating a new API key.","properties":{"allowed_models":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Allowed Models"},"allowed_tools":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Allowed Tools"},"capture_agent_telemetry":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Capture Agent Telemetry"},"created_at":{"title":"Created At","type":"string"},"end_user_budget_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"End User Budget Id"},"end_user_budget_ids":{"items":{"type":"string"},"title":"End User Budget Ids","type":"array"},"exclude_from_budget":{"title":"Exclude From Budget","type":"boolean"},"expires_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expires At"},"id":{"title":"Id","type":"string"},"is_active":{"title":"Is Active","type":"boolean"},"is_service_key":{"title":"Is Service Key","type":"boolean"},"key":{"title":"Key","type":"string"},"key_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key Name"},"key_prefix":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key Prefix"},"key_suffix":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key Suffix"},"max_end_users":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max End Users"},"metadata":{"additionalProperties":true,"title":"Metadata","type":"object"},"reject_user_mismatch":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Reject User Mismatch"},"user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id"}},"required":["id","key","key_prefix","key_suffix","key_name","user_id","created_at","expires_at","is_active","allowed_models","allowed_tools","exclude_from_budget","reject_user_mismatch","capture_agent_telemetry","is_service_key","end_user_budget_id","end_user_budget_ids","max_end_users","metadata"],"title":"CreateKeyResponse","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/keys":{"post":{"description":"Create a new API key in the caller's organization.\n\nRequires master key authentication.\n\nIf user_id is provided, the key will be associated with that user (creates user if it doesn't exist).\nIf user_id is not provided, the key is associated with the shared \"default\" user, which is created\non first use. Keys without an explicit owner therefore share one identity, and so share budget,\nusage, and files.\n\n``workspace_id`` names a workspace in the caller's organization, and omitting\nit mints into that organization's default workspace. A key resolves that\norganization's provider credentials and bills there, so minting into another\norganization's workspace would spend its budget on its credentials.","operationId":"keys-create_key","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateKeyRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateKeyResponse"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Create Key","tags":["keys"]}}}}
```

## Get Key

> Get details of a specific API key in the caller's organization.\
> \
> Requires master key authentication.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"KeyInfo":{"description":"Response model for key information.","properties":{"allowed_models":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Allowed Models"},"allowed_tools":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Allowed Tools"},"capture_agent_telemetry":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Capture Agent Telemetry"},"config_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Config Name"},"created_at":{"title":"Created At","type":"string"},"end_user_budget_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"End User Budget Id"},"end_user_budget_ids":{"items":{"type":"string"},"title":"End User Budget Ids","type":"array"},"exclude_from_budget":{"title":"Exclude From Budget","type":"boolean"},"expires_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expires At"},"id":{"title":"Id","type":"string"},"is_active":{"title":"Is Active","type":"boolean"},"is_service_key":{"title":"Is Service Key","type":"boolean"},"key_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key Name"},"key_prefix":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key Prefix"},"key_suffix":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key Suffix"},"last_used_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Used At"},"max_end_users":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max End Users"},"metadata":{"additionalProperties":true,"title":"Metadata","type":"object"},"operator_disabled":{"default":false,"title":"Operator Disabled","type":"boolean"},"reject_user_mismatch":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Reject User Mismatch"},"user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id"},"workspace_id":{"format":"uuid","title":"Workspace Id","type":"string"}},"required":["id","key_prefix","key_suffix","key_name","user_id","created_at","last_used_at","expires_at","is_active","allowed_models","allowed_tools","exclude_from_budget","reject_user_mismatch","capture_agent_telemetry","is_service_key","end_user_budget_id","end_user_budget_ids","workspace_id","metadata"],"title":"KeyInfo","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/keys/{key_id}":{"get":{"description":"Get details of a specific API key in the caller's organization.\n\nRequires master key authentication.","operationId":"keys-get_key","parameters":[{"in":"path","name":"key_id","required":true,"schema":{"title":"Key Id","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeyInfo"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Get Key","tags":["keys"]}}}}
```

## Update Key

> Update an API key in the caller's organization.\
> \
> Requires master key authentication.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"UpdateKeyRequest":{"description":"Request model for updating a key.","properties":{"allowed_models":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Allowed Models"},"allowed_tools":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Allowed Tools"},"capture_agent_telemetry":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Capture Agent Telemetry"},"end_user_budget_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"End User Budget Id"},"end_user_budget_ids":{"anyOf":[{"items":{"type":"string"},"maxItems":100,"type":"array"},{"type":"null"}],"title":"End User Budget Ids"},"exclude_from_budget":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Exclude From Budget"},"expires_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"title":"Expires At"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"},"is_service_key":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Service Key"},"key_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key Name"},"max_end_users":{"anyOf":[{"minimum":0,"type":"integer"},{"type":"null"}],"title":"Max End Users"},"metadata":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Metadata"},"reject_user_mismatch":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Reject User Mismatch"}},"title":"UpdateKeyRequest","type":"object"},"KeyInfo":{"description":"Response model for key information.","properties":{"allowed_models":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Allowed Models"},"allowed_tools":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Allowed Tools"},"capture_agent_telemetry":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Capture Agent Telemetry"},"config_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Config Name"},"created_at":{"title":"Created At","type":"string"},"end_user_budget_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"End User Budget Id"},"end_user_budget_ids":{"items":{"type":"string"},"title":"End User Budget Ids","type":"array"},"exclude_from_budget":{"title":"Exclude From Budget","type":"boolean"},"expires_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expires At"},"id":{"title":"Id","type":"string"},"is_active":{"title":"Is Active","type":"boolean"},"is_service_key":{"title":"Is Service Key","type":"boolean"},"key_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key Name"},"key_prefix":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key Prefix"},"key_suffix":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key Suffix"},"last_used_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Used At"},"max_end_users":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max End Users"},"metadata":{"additionalProperties":true,"title":"Metadata","type":"object"},"operator_disabled":{"default":false,"title":"Operator Disabled","type":"boolean"},"reject_user_mismatch":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Reject User Mismatch"},"user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id"},"workspace_id":{"format":"uuid","title":"Workspace Id","type":"string"}},"required":["id","key_prefix","key_suffix","key_name","user_id","created_at","last_used_at","expires_at","is_active","allowed_models","allowed_tools","exclude_from_budget","reject_user_mismatch","capture_agent_telemetry","is_service_key","end_user_budget_id","end_user_budget_ids","workspace_id","metadata"],"title":"KeyInfo","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/keys/{key_id}":{"patch":{"description":"Update an API key in the caller's organization.\n\nRequires master key authentication.","operationId":"keys-update_key","parameters":[{"in":"path","name":"key_id","required":true,"schema":{"title":"Key Id","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateKeyRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeyInfo"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Update Key","tags":["keys"]}}}}
```

## Delete Key

> Delete (revoke) an API key in the caller's organization.\
> \
> Requires master key authentication.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/keys/{key_id}":{"delete":{"description":"Delete (revoke) an API key in the caller's organization.\n\nRequires master key authentication.","operationId":"keys-delete_key","parameters":[{"in":"path","name":"key_id","required":true,"schema":{"title":"Key Id","type":"string"}}],"responses":{"204":{"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Delete Key","tags":["keys"]}}}}
```

## List End Users

> List the end users of a service key, oldest first.\
> \
> End users belong to the key's user, so every service key of one user lists the same end users. A deleted end\
> user is not listed. \`\`external\_id\`\` reads one end user by any id, one containing '/' included, as a list of zero\
> or one.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"EndUserPublic":{"description":"An end user of a service key, addressed by the id the service named it by.","properties":{"blocked":{"title":"Blocked","type":"boolean"},"budget_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Budget Id"},"budget_started_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Budget Started At"},"created_at":{"title":"Created At","type":"string"},"current_requests":{"title":"Current Requests","type":"integer"},"current_tokens":{"title":"Current Tokens","type":"integer"},"external_id":{"title":"External Id","type":"string"},"next_budget_reset_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Budget Reset At"},"owner_user_id":{"title":"Owner User Id","type":"string"},"reserved":{"title":"Reserved","type":"number"},"spend":{"title":"Spend","type":"number"},"user_id":{"title":"User Id","type":"string"}},"required":["user_id","external_id","owner_user_id","budget_id","blocked","spend","reserved","current_tokens","current_requests","budget_started_at","next_budget_reset_at","created_at"],"title":"EndUserPublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/keys/{key_id}/end-users":{"get":{"description":"List the end users of a service key, oldest first.\n\nEnd users belong to the key's user, so every service key of one user lists the same end users. A deleted end\nuser is not listed. ``external_id`` reads one end user by any id, one containing '/' included, as a list of zero\nor one.","operationId":"keys-list_end_users","parameters":[{"in":"path","name":"key_id","required":true,"schema":{"title":"Key Id","type":"string"}},{"in":"query","name":"skip","required":false,"schema":{"default":0,"minimum":0,"title":"Skip","type":"integer"}},{"in":"query","name":"limit","required":false,"schema":{"default":100,"maximum":1000,"minimum":1,"title":"Limit","type":"integer"}},{"description":"Only end users on this budget.","in":"query","name":"budget_id","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Only end users on this budget.","title":"Budget Id"}},{"description":"Only end users that are, or are not, blocked.","in":"query","name":"blocked","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Only end users that are, or are not, blocked.","title":"Blocked"}},{"description":"Only the end user the service names with this `user` value: zero or one.","in":"query","name":"external_id","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Only the end user the service names with this `user` value: zero or one.","title":"External Id"}},{"description":"Also count every matching end user, in the Otari-Total-Count response header.","in":"query","name":"include_total","required":false,"schema":{"default":false,"description":"Also count every matching end user, in the Otari-Total-Count response header.","title":"Include Total","type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/EndUserPublic"},"title":"Response Keys-List End Users","type":"array"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"List End Users","tags":["keys"]}}}}
```

## Put End User By Query

> Put the end user named by \`\`external\_id\`\` on a budget from the key's list, creating it if it does not exist yet.\
> \
> The same as \`\`PUT /keys/{key\_id}/end-users/{external\_id}\`\`, for any id, one containing '/' included.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"EndUserPut":{"description":"Create an end user ahead of its first request, or move one, onto a budget on the key's list.","properties":{"budget_id":{"description":"A budget on the key's end_user_budget_ids","minLength":1,"title":"Budget Id","type":"string"}},"required":["budget_id"],"title":"EndUserPut","type":"object"},"EndUserPublic":{"description":"An end user of a service key, addressed by the id the service named it by.","properties":{"blocked":{"title":"Blocked","type":"boolean"},"budget_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Budget Id"},"budget_started_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Budget Started At"},"created_at":{"title":"Created At","type":"string"},"current_requests":{"title":"Current Requests","type":"integer"},"current_tokens":{"title":"Current Tokens","type":"integer"},"external_id":{"title":"External Id","type":"string"},"next_budget_reset_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Budget Reset At"},"owner_user_id":{"title":"Owner User Id","type":"string"},"reserved":{"title":"Reserved","type":"number"},"spend":{"title":"Spend","type":"number"},"user_id":{"title":"User Id","type":"string"}},"required":["user_id","external_id","owner_user_id","budget_id","blocked","spend","reserved","current_tokens","current_requests","budget_started_at","next_budget_reset_at","created_at"],"title":"EndUserPublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/keys/{key_id}/end-users":{"put":{"description":"Put the end user named by ``external_id`` on a budget from the key's list, creating it if it does not exist yet.\n\nThe same as ``PUT /keys/{key_id}/end-users/{external_id}``, for any id, one containing '/' included.","operationId":"keys-put_end_user_by_query","parameters":[{"in":"path","name":"key_id","required":true,"schema":{"title":"Key Id","type":"string"}},{"description":"The id the service names the end user by in a request's user field, any id, '/' included","in":"query","name":"external_id","required":true,"schema":{"description":"The id the service names the end user by in a request's user field, any id, '/' included","minLength":1,"title":"External Id","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EndUserPut"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EndUserPublic"}}},"description":"Successful Response"},"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EndUserPublic"}}},"description":"The end user was created"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Put End User By Query","tags":["keys"]}}}}
```

## Update End User By Query

> Block, unblock or move the end user of a service key named by \`\`external\_id\`\`.\
> \
> The same as \`\`PATCH /keys/{key\_id}/end-users/{external\_id}\`\`, for any id, one containing '/' included.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"EndUserUpdate":{"description":"Block, unblock or move an end user. An omitted field is left as it is.","properties":{"blocked":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Whether the end user is refused","title":"Blocked"},"budget_id":{"anyOf":[{"minLength":1,"type":"string"},{"type":"null"}],"description":"A budget on the key's end_user_budget_ids to move the end user to","title":"Budget Id"}},"title":"EndUserUpdate","type":"object"},"EndUserPublic":{"description":"An end user of a service key, addressed by the id the service named it by.","properties":{"blocked":{"title":"Blocked","type":"boolean"},"budget_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Budget Id"},"budget_started_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Budget Started At"},"created_at":{"title":"Created At","type":"string"},"current_requests":{"title":"Current Requests","type":"integer"},"current_tokens":{"title":"Current Tokens","type":"integer"},"external_id":{"title":"External Id","type":"string"},"next_budget_reset_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Budget Reset At"},"owner_user_id":{"title":"Owner User Id","type":"string"},"reserved":{"title":"Reserved","type":"number"},"spend":{"title":"Spend","type":"number"},"user_id":{"title":"User Id","type":"string"}},"required":["user_id","external_id","owner_user_id","budget_id","blocked","spend","reserved","current_tokens","current_requests","budget_started_at","next_budget_reset_at","created_at"],"title":"EndUserPublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/keys/{key_id}/end-users":{"patch":{"description":"Block, unblock or move the end user of a service key named by ``external_id``.\n\nThe same as ``PATCH /keys/{key_id}/end-users/{external_id}``, for any id, one containing '/' included.","operationId":"keys-update_end_user_by_query","parameters":[{"in":"path","name":"key_id","required":true,"schema":{"title":"Key Id","type":"string"}},{"description":"The id the service names the end user by in a request's user field, any id, '/' included","in":"query","name":"external_id","required":true,"schema":{"description":"The id the service names the end user by in a request's user field, any id, '/' included","minLength":1,"title":"External Id","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EndUserUpdate"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EndUserPublic"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Update End User By Query","tags":["keys"]}}}}
```

## Get End User

> Get an end user of a service key by the id the service names it by.\
> \
> End users belong to the key's user, so every service key of one user reaches the same end users. An id with a\
> '.' or '..' segment is refused with 400; \`\`GET /keys/{key\_id}/end-users?external\_id=\`\` reads any end user.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"EndUserPublic":{"description":"An end user of a service key, addressed by the id the service named it by.","properties":{"blocked":{"title":"Blocked","type":"boolean"},"budget_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Budget Id"},"budget_started_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Budget Started At"},"created_at":{"title":"Created At","type":"string"},"current_requests":{"title":"Current Requests","type":"integer"},"current_tokens":{"title":"Current Tokens","type":"integer"},"external_id":{"title":"External Id","type":"string"},"next_budget_reset_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Budget Reset At"},"owner_user_id":{"title":"Owner User Id","type":"string"},"reserved":{"title":"Reserved","type":"number"},"spend":{"title":"Spend","type":"number"},"user_id":{"title":"User Id","type":"string"}},"required":["user_id","external_id","owner_user_id","budget_id","blocked","spend","reserved","current_tokens","current_requests","budget_started_at","next_budget_reset_at","created_at"],"title":"EndUserPublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/keys/{key_id}/end-users/{external_id}":{"get":{"description":"Get an end user of a service key by the id the service names it by.\n\nEnd users belong to the key's user, so every service key of one user reaches the same end users. An id with a\n'.' or '..' segment is refused with 400; ``GET /keys/{key_id}/end-users?external_id=`` reads any end user.","operationId":"keys-get_end_user","parameters":[{"in":"path","name":"key_id","required":true,"schema":{"title":"Key Id","type":"string"}},{"description":"The id the service names the end user by in a request's user field","in":"path","name":"external_id","required":true,"schema":{"description":"The id the service names the end user by in a request's user field","title":"External Id","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EndUserPublic"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Get End User","tags":["keys"]}}}}
```

## Put End User

> Put an end user of a service key on a budget from the key's list, creating it if it does not exist yet.\
> \
> Answers 201 when the end user was created, so one can be placed on a budget before its first request. An end\
> user already on the budget keeps its current period, so repeating the call changes nothing. A budget that is\
> not on the key's \`\`end\_user\_budget\_ids\`\` is refused with 403 and \`\`end\_user\_budget\_not\_allowed\`\`. An id with a\
> '.' or '..' segment is refused with 400; \`\`PUT /keys/{key\_id}/end-users?external\_id=\`\` takes any id.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"EndUserPut":{"description":"Create an end user ahead of its first request, or move one, onto a budget on the key's list.","properties":{"budget_id":{"description":"A budget on the key's end_user_budget_ids","minLength":1,"title":"Budget Id","type":"string"}},"required":["budget_id"],"title":"EndUserPut","type":"object"},"EndUserPublic":{"description":"An end user of a service key, addressed by the id the service named it by.","properties":{"blocked":{"title":"Blocked","type":"boolean"},"budget_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Budget Id"},"budget_started_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Budget Started At"},"created_at":{"title":"Created At","type":"string"},"current_requests":{"title":"Current Requests","type":"integer"},"current_tokens":{"title":"Current Tokens","type":"integer"},"external_id":{"title":"External Id","type":"string"},"next_budget_reset_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Budget Reset At"},"owner_user_id":{"title":"Owner User Id","type":"string"},"reserved":{"title":"Reserved","type":"number"},"spend":{"title":"Spend","type":"number"},"user_id":{"title":"User Id","type":"string"}},"required":["user_id","external_id","owner_user_id","budget_id","blocked","spend","reserved","current_tokens","current_requests","budget_started_at","next_budget_reset_at","created_at"],"title":"EndUserPublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/keys/{key_id}/end-users/{external_id}":{"put":{"description":"Put an end user of a service key on a budget from the key's list, creating it if it does not exist yet.\n\nAnswers 201 when the end user was created, so one can be placed on a budget before its first request. An end\nuser already on the budget keeps its current period, so repeating the call changes nothing. A budget that is\nnot on the key's ``end_user_budget_ids`` is refused with 403 and ``end_user_budget_not_allowed``. An id with a\n'.' or '..' segment is refused with 400; ``PUT /keys/{key_id}/end-users?external_id=`` takes any id.","operationId":"keys-put_end_user","parameters":[{"in":"path","name":"key_id","required":true,"schema":{"title":"Key Id","type":"string"}},{"description":"The id the service names the end user by in a request's user field","in":"path","name":"external_id","required":true,"schema":{"description":"The id the service names the end user by in a request's user field","title":"External Id","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EndUserPut"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EndUserPublic"}}},"description":"Successful Response"},"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EndUserPublic"}}},"description":"The end user was created"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Put End User","tags":["keys"]}}}}
```

## Update End User

> Block, unblock or move an end user of a service key.\
> \
> A move restarts the end user's period on the new budget but keeps what it has spent and used so far, as the users\
> API does. The budget must be on the key's \`\`end\_user\_budget\_ids\`\`. An id with a '.' or '..' segment is refused\
> with 400; \`\`PATCH /keys/{key\_id}/end-users?external\_id=\`\` takes any id.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"EndUserUpdate":{"description":"Block, unblock or move an end user. An omitted field is left as it is.","properties":{"blocked":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Whether the end user is refused","title":"Blocked"},"budget_id":{"anyOf":[{"minLength":1,"type":"string"},{"type":"null"}],"description":"A budget on the key's end_user_budget_ids to move the end user to","title":"Budget Id"}},"title":"EndUserUpdate","type":"object"},"EndUserPublic":{"description":"An end user of a service key, addressed by the id the service named it by.","properties":{"blocked":{"title":"Blocked","type":"boolean"},"budget_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Budget Id"},"budget_started_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Budget Started At"},"created_at":{"title":"Created At","type":"string"},"current_requests":{"title":"Current Requests","type":"integer"},"current_tokens":{"title":"Current Tokens","type":"integer"},"external_id":{"title":"External Id","type":"string"},"next_budget_reset_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Budget Reset At"},"owner_user_id":{"title":"Owner User Id","type":"string"},"reserved":{"title":"Reserved","type":"number"},"spend":{"title":"Spend","type":"number"},"user_id":{"title":"User Id","type":"string"}},"required":["user_id","external_id","owner_user_id","budget_id","blocked","spend","reserved","current_tokens","current_requests","budget_started_at","next_budget_reset_at","created_at"],"title":"EndUserPublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/keys/{key_id}/end-users/{external_id}":{"patch":{"description":"Block, unblock or move an end user of a service key.\n\nA move restarts the end user's period on the new budget but keeps what it has spent and used so far, as the users\nAPI does. The budget must be on the key's ``end_user_budget_ids``. An id with a '.' or '..' segment is refused\nwith 400; ``PATCH /keys/{key_id}/end-users?external_id=`` takes any id.","operationId":"keys-update_end_user","parameters":[{"in":"path","name":"key_id","required":true,"schema":{"title":"Key Id","type":"string"}},{"description":"The id the service names the end user by in a request's user field","in":"path","name":"external_id","required":true,"schema":{"description":"The id the service names the end user by in a request's user field","title":"External Id","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EndUserUpdate"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EndUserPublic"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Update End User","tags":["keys"]}}}}
```

## List Federation Rules

> List the identities that may authenticate as this key, oldest first.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"FederationRulePublic":{"description":"A federation rule of an API key.","properties":{"api_key_id":{"title":"Api Key Id","type":"string"},"audience":{"title":"Audience","type":"string"},"created_at":{"format":"date-time","title":"Created At","type":"string"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"id":{"title":"Id","type":"string"},"issuer":{"title":"Issuer","type":"string"},"member_managed":{"description":"Whether a member gave the rule to their own key: it then matches only in the token exchange.","title":"Member Managed","type":"boolean"},"subject":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subject"}},"required":["id","api_key_id","issuer","audience","subject","email","member_managed","created_at"],"title":"FederationRulePublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/keys/{key_id}/federation-rules":{"get":{"description":"List the identities that may authenticate as this key, oldest first.","operationId":"keys-list_federation_rules","parameters":[{"in":"path","name":"key_id","required":true,"schema":{"title":"Key Id","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/FederationRulePublic"},"title":"Response Keys-List Federation Rules","type":"array"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"List Federation Rules","tags":["keys"]}}}}
```

## Create Federation Rule

> Let an OpenID Connect identity token authenticate as this key, in place of its secret.\
> \
> The issuer must be one of the deployment's \`\`federation\_issuers\`\`, else 400. A token authenticates when it is\
> signed by that issuer, its \`\`aud\`\` contains \`\`audience\`\`, and it carries the rule's \`\`subject\`\` and verified\
> \`\`email\`\`, whichever the rule sets. A rule naming the identity of an existing rule answers 409: the same issuer,\
> audience, subject and email on this key, or on another key the same issuer and audience with a subject or email\
> in common and no contradicting value.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"FederationRuleCreate":{"additionalProperties":false,"description":"An OpenID Connect identity that may authenticate as the key, in place of its secret.\n\nA token matches when it is signed by ``issuer``, names ``audience`` among its audiences, and carries each of\n``subject`` and ``email`` that the rule sets; an email matches only when the token marks it verified. At least\none of the two is required.","properties":{"audience":{"description":"A value the token's 'aud' must contain.","maxLength":512,"minLength":1,"title":"Audience","type":"string"},"email":{"anyOf":[{"maxLength":320,"minLength":3,"type":"string"},{"type":"null"}],"description":"The token's verified 'email', when the rule names one. Compared without regard to case.","title":"Email"},"issuer":{"description":"The token's 'iss'. On an operator's rule, one of the deployment's federation_issuers; on a member's own key, any https issuer, whose discovery document and signing keys are fetched only from a public host.","maxLength":512,"minLength":1,"title":"Issuer","type":"string"},"subject":{"anyOf":[{"maxLength":255,"minLength":1,"type":"string"},{"type":"null"}],"description":"The token's 'sub', when the rule names one.","title":"Subject"}},"required":["issuer","audience"],"title":"FederationRuleCreate","type":"object"},"FederationRulePublic":{"description":"A federation rule of an API key.","properties":{"api_key_id":{"title":"Api Key Id","type":"string"},"audience":{"title":"Audience","type":"string"},"created_at":{"format":"date-time","title":"Created At","type":"string"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"id":{"title":"Id","type":"string"},"issuer":{"title":"Issuer","type":"string"},"member_managed":{"description":"Whether a member gave the rule to their own key: it then matches only in the token exchange.","title":"Member Managed","type":"boolean"},"subject":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subject"}},"required":["id","api_key_id","issuer","audience","subject","email","member_managed","created_at"],"title":"FederationRulePublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/keys/{key_id}/federation-rules":{"post":{"description":"Let an OpenID Connect identity token authenticate as this key, in place of its secret.\n\nThe issuer must be one of the deployment's ``federation_issuers``, else 400. A token authenticates when it is\nsigned by that issuer, its ``aud`` contains ``audience``, and it carries the rule's ``subject`` and verified\n``email``, whichever the rule sets. A rule naming the identity of an existing rule answers 409: the same issuer,\naudience, subject and email on this key, or on another key the same issuer and audience with a subject or email\nin common and no contradicting value.","operationId":"keys-create_federation_rule","parameters":[{"in":"path","name":"key_id","required":true,"schema":{"title":"Key Id","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FederationRuleCreate"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FederationRulePublic"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Create Federation Rule","tags":["keys"]}}}}
```

## Delete Federation Rule

> Remove a federation rule. Tokens it matched stop authenticating with the next request.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/keys/{key_id}/federation-rules/{rule_id}":{"delete":{"description":"Remove a federation rule. Tokens it matched stop authenticating with the next request.","operationId":"keys-delete_federation_rule","parameters":[{"in":"path","name":"key_id","required":true,"schema":{"title":"Key Id","type":"string"}},{"in":"path","name":"rule_id","required":true,"schema":{"title":"Rule Id","type":"string"}}],"responses":{"204":{"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Delete Federation Rule","tags":["keys"]}}}}
```

## Rotate Key

> Rotate an API key's secret in place, within the caller's organization.\
> \
> Requires master key authentication.\
> \
> Generates a new secret for the same key row (id, user, name, expiry, and\
> metadata are preserved) and returns the new raw key once, using the same\
> response shape as key creation. The previous secret stops authenticating\
> immediately; there is no grace window.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"CreateKeyResponse":{"description":"Response model for creating a new API key.","properties":{"allowed_models":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Allowed Models"},"allowed_tools":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Allowed Tools"},"capture_agent_telemetry":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Capture Agent Telemetry"},"created_at":{"title":"Created At","type":"string"},"end_user_budget_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"End User Budget Id"},"end_user_budget_ids":{"items":{"type":"string"},"title":"End User Budget Ids","type":"array"},"exclude_from_budget":{"title":"Exclude From Budget","type":"boolean"},"expires_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expires At"},"id":{"title":"Id","type":"string"},"is_active":{"title":"Is Active","type":"boolean"},"is_service_key":{"title":"Is Service Key","type":"boolean"},"key":{"title":"Key","type":"string"},"key_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key Name"},"key_prefix":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key Prefix"},"key_suffix":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key Suffix"},"max_end_users":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max End Users"},"metadata":{"additionalProperties":true,"title":"Metadata","type":"object"},"reject_user_mismatch":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Reject User Mismatch"},"user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id"}},"required":["id","key","key_prefix","key_suffix","key_name","user_id","created_at","expires_at","is_active","allowed_models","allowed_tools","exclude_from_budget","reject_user_mismatch","capture_agent_telemetry","is_service_key","end_user_budget_id","end_user_budget_ids","max_end_users","metadata"],"title":"CreateKeyResponse","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/keys/{key_id}/rotate":{"post":{"description":"Rotate an API key's secret in place, within the caller's organization.\n\nRequires master key authentication.\n\nGenerates a new secret for the same key row (id, user, name, expiry, and\nmetadata are preserved) and returns the new raw key once, using the same\nresponse shape as key creation. The previous secret stops authenticating\nimmediately; there is no grace window.","operationId":"keys-rotate_key","parameters":[{"in":"path","name":"key_id","required":true,"schema":{"title":"Key Id","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateKeyResponse"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Rotate Key","tags":["keys"]}}}}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.otari.ai/api-reference/keys.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
