diff --git a/.speakeasy/gen.lock b/.speakeasy/gen.lock
index 1e6bb715..2b29a829 100644
--- a/.speakeasy/gen.lock
+++ b/.speakeasy/gen.lock
@@ -1,19 +1,19 @@
lockVersion: 2.0.0
id: c48cf606-fb42-4a45-9c23-8f0555307828
management:
- docChecksum: 63ba3216b2499bb104f5ba497212cf31
+ docChecksum: ecb5eca3ef6a267213db0ded757b55e3
docVersion: 1.0.0
speakeasyVersion: 1.787.0
generationVersion: 2.914.0
- releaseVersion: 1.3.39
- configChecksum: 087e7677c4446238e5e3330e10a2f485
+ releaseVersion: 1.3.40
+ configChecksum: ee9a5a783a8eacb732f771decfd8b7db
repoURL: https://github.com/OpenRouterTeam/python-sdk.git
installationURL: https://github.com/OpenRouterTeam/python-sdk.git
published: true
persistentEdits:
- generation_id: 7bb99ffd-de0f-43a3-bedf-0b7ba639e3ee
- pristine_commit_hash: a065177d322362e8b66a40bae6ae5286f280d0d7
- pristine_tree_hash: 59713b5944267b079a2cd2c4f9461d4d1e958909
+ generation_id: 71c9c96b-57d8-43db-a7fd-358f31fd9922
+ pristine_commit_hash: 56313cab4b918fcdba448e161f1d1440024301e6
+ pristine_tree_hash: 6a298d935c5177a5636b85c8c413ee28b7dcbe89
features:
python:
acceptHeaders: 3.0.0
@@ -9796,8 +9796,8 @@ trackedFiles:
pristine_git_object: e6d3d7f0130773c07971473e1bc4e536b403a993
docs/operations/updatekeysrequestbody.mdx:
id: dc55066cca3d
- last_write_checksum: sha1:28fd19213cd690e7aec9fa106db1b835094a9d3a
- pristine_git_object: b293f36e7709b7bebad59271db0885d29711c2d9
+ last_write_checksum: sha1:6a8c00577467c533b49ce9d0a4a1742993730034
+ pristine_git_object: bc08a2d46ccca8a3a8d61d196611ecc7c0782903
docs/operations/updatekeysresponse.mdx:
id: a97d51fea627
last_write_checksum: sha1:e3064726562fa278b17b113764602a46e0919ffe
@@ -9920,8 +9920,8 @@ trackedFiles:
pristine_git_object: c1634706646332455817340f5891b4b5c9c6d16c
docs/sdks/apikeys/README.mdx:
id: 27fcaae1c346
- last_write_checksum: sha1:dece70cc0e48aa3e8f523d4b27844e1f870d1b2d
- pristine_git_object: 211cfc120a8941c8006179541b6ba9bb8599f661
+ last_write_checksum: sha1:d904a5734d4cf752acf932c54fa8cb714034e38c
+ pristine_git_object: 558caa92f560fd87cb7ed6bb6d1989b7a5001c81
docs/sdks/batch/README.mdx:
id: e367aa4f4295
last_write_checksum: sha1:03ef59395c1bc1a4ffa14dd68ca2a77a72601080
@@ -10068,8 +10068,8 @@ trackedFiles:
pristine_git_object: 3e38f1a929f7d6b1d6de74604aa87e3d8f010544
pyproject.toml:
id: 5d07e7d72637
- last_write_checksum: sha1:ae86206d341aa777b7b704f3b55146ddaa47ed1e
- pristine_git_object: 603917d374eaecf1b6cf8f220f107be47c2d3793
+ last_write_checksum: sha1:13e86e0bb1241458142202f2f8f09b882ed9eaef
+ pristine_git_object: 58dec50a7d7a3657ca82dc463ebe5868b50a680c
scripts/prepare_readme.py:
id: e0c5957a6035
last_write_checksum: sha1:77f44b60b98bc126557ec27391f91dfba764bb54
@@ -10096,8 +10096,8 @@ trackedFiles:
pristine_git_object: 86713cfea633e09d33b3d4e65281071fe20e6137
src/openrouter/_version.py:
id: d8d15ad6c586
- last_write_checksum: sha1:384892b86cee88cd2af1039fc03d15de610578af
- pristine_git_object: 0cf6cc22eabc83b4cefb2d11d0a175413bbbed9d
+ last_write_checksum: sha1:c6901839a49042fdd00a20f0200548c0cd92d0a0
+ pristine_git_object: b42d7fe103d72102be9150bae1c462b64e09d461
src/openrouter/alpha.py:
id: 306c4d93308d
last_write_checksum: sha1:30f55a360f41376ab194b9ea725fe4e001a5ae1a
@@ -10108,8 +10108,8 @@ trackedFiles:
pristine_git_object: 9587309db4d5e25a00131a48788456139c25e4c0
src/openrouter/api_keys.py:
id: 876fac88fff6
- last_write_checksum: sha1:42bbc2aadb2722a96eba3c83eff26afcfa6a0572
- pristine_git_object: 1b9ba98c188a8887d8dd27bc9f200deaacdcdef6
+ last_write_checksum: sha1:be6e31bd6aa3b8cacec826ba4b249739b4510b79
+ pristine_git_object: 3c95e38bd085b026382bace5eab99a7cc91af00a
src/openrouter/basesdk.py:
id: c5733c5a1e12
last_write_checksum: sha1:65769ff2cb5473f85b364c6213a0ddc8c68644ea
@@ -14288,8 +14288,8 @@ trackedFiles:
pristine_git_object: d87519cfb18258224c2958076dedb16ff4abe208
src/openrouter/operations/updatekeys.py:
id: 56fb213253a8
- last_write_checksum: sha1:d70e4b1e06f8b0e1569a172e263b3e541edf4442
- pristine_git_object: 3862d92575f681cc5eb43fd27d3d57114972ed2e
+ last_write_checksum: sha1:8a49a1d8fecd7314d177d488ecec30214f4e918b
+ pristine_git_object: e9ed9da08d2a39aa0f9dc295fdd4e006e8dd49f5
src/openrouter/operations/updateobservabilitydestination.py:
id: 18e06708d1b4
last_write_checksum: sha1:d490e19e8698ed5193df9d06673f975cb3a1410f
@@ -14807,6 +14807,10 @@ examples:
application/json: {"error": {"code": 429, "message": "Rate limit exceeded"}}
"500":
application/json: {"error": {"code": 500, "message": "Internal Server Error"}}
+ "403":
+ application/json: {"error": {"code": 403, "message": "Only management keys can perform this operation"}}
+ "409":
+ application/json: {"error": {"code": 409, "message": "Resource conflict. Please try again later."}}
invalid_parameters:
parameters:
path:
@@ -18432,3 +18436,4 @@ examples:
"500":
application/json: {"error": {"code": 404, "message": "Intern not found"}}
examplesVersion: 1.0.2
+releaseNotes: "## Python SDK Changes:\n* `open_router.api_keys.update()`: \n * `request.workspace_id` **Added**\n * `error` **Changed**\n"
diff --git a/.speakeasy/gen.yaml b/.speakeasy/gen.yaml
index f67817a7..c2d663d1 100644
--- a/.speakeasy/gen.yaml
+++ b/.speakeasy/gen.yaml
@@ -36,7 +36,7 @@ generation:
documentation: mintlify
preApplyUnionDiscriminators: true
python:
- version: 1.3.39
+ version: 1.3.40
additionalDependencies:
dev: {}
main: {}
diff --git a/.speakeasy/out.openapi.yaml b/.speakeasy/out.openapi.yaml
index aa58935c..d32e9c3e 100644
--- a/.speakeasy/out.openapi.yaml
+++ b/.speakeasy/out.openapi.yaml
@@ -44741,7 +44741,7 @@ paths:
- 'API Keys'
x-speakeasy-name-override: 'get'
patch:
- description: "Update an existing API key. Authenticate with a [management key](/docs/guides/overview/auth/management-api-keys).\n\n\nYou can't change `workspace_id` through the API. The request body accepts only the fields listed below, and unrecognized fields are ignored. To move a key to another workspace, use the OpenRouter dashboard. If the body contains none of the accepted fields, the request fails with `400` and the message `No update fields provided`.\n"
+ description: "Update an existing API key. Authenticate with a [management key](/docs/guides/overview/auth/management-api-keys).\n\nSet `workspace_id` to move the key to another workspace. The key keeps its value, and any other fields in the same request are applied atomically with the move. Guardrail selections move with the key; other workspace-scoped settings (presets, BYOK keys, broadcast destinations, routing rules) do not. A move into or out of a HIPAA workspace is refused with `403`. In an organization, if the key's creator is not a member of a non-default target workspace, the request fails with `409`; add them to the workspace first.\n\n\nThe request body accepts only the fields listed below, and unrecognized fields are ignored. If the body contains none of the accepted fields, the request fails with `400` and the message `No update fields provided`.\n"
operationId: 'updateKeys'
parameters:
- description: 'The hash identifier of the API key to update'
@@ -44800,6 +44800,11 @@ paths:
description: 'New name for the API key'
example: 'Updated API Key Name'
type: 'string'
+ workspace_id:
+ description: 'Move the API key to this workspace. The key keeps its value; guardrail selections move with it, while other workspace-scoped settings (presets, BYOK keys, broadcast destinations, routing rules) do not. Sending the key''s current workspace is a no-op.'
+ example: '0df9e665-d932-5740-b2c7-b52af166bc11'
+ format: 'uuid'
+ type: 'string'
type: 'object'
required: true
responses:
@@ -45043,7 +45048,7 @@ paths:
code: 400
message: 'Invalid request parameters'
no_update_fields:
- description: 'Returned when the body contains none of the accepted fields, for example `{"workspace_id": "..."}`. `workspace_id` can''t be changed through the API.'
+ description: 'Returned when the body contains none of the accepted fields, for example `{"label": "..."}`.'
summary: 'No accepted fields in the request body'
value:
error:
@@ -45062,6 +45067,16 @@ paths:
schema:
$ref: '#/components/schemas/UnauthorizedResponse'
description: 'Unauthorized - Authentication required or invalid credentials'
+ '403':
+ content:
+ application/json:
+ example:
+ error:
+ code: 403
+ message: 'Only management keys can perform this operation'
+ schema:
+ $ref: '#/components/schemas/ForbiddenResponse'
+ description: 'Forbidden - Authentication successful but insufficient permissions'
'404':
content:
application/json:
@@ -45072,6 +45087,16 @@ paths:
schema:
$ref: '#/components/schemas/NotFoundResponse'
description: 'Not Found - Resource does not exist'
+ '409':
+ content:
+ application/json:
+ example:
+ error:
+ code: 409
+ message: 'The API key creator is not a member of the target workspace. Add them to the workspace, then retry.'
+ schema:
+ $ref: '#/components/schemas/ConflictResponse'
+ description: 'Conflict - Resource conflict or concurrent modification'
'429':
content:
application/json:
diff --git a/.speakeasy/workflow.lock b/.speakeasy/workflow.lock
index c10869d1..ac8a9ac0 100644
--- a/.speakeasy/workflow.lock
+++ b/.speakeasy/workflow.lock
@@ -2,8 +2,8 @@ speakeasyVersion: 1.787.0
sources:
OpenRouter API:
sourceNamespace: open-router-chat-completions-api
- sourceRevisionDigest: sha256:cf24edf70629b35aad73aef00830d7d2f92f5f199bd6540c50f4887f848a33da
- sourceBlobDigest: sha256:8c7c009a8ce7e8466887920f6d8c5246378189395521adf6e1a0464a23cf2caa
+ sourceRevisionDigest: sha256:c597251b8315aeabdac8771a04fa223bbd27a7ee37c50e7865f78a63b009cd05
+ sourceBlobDigest: sha256:63d088f642ae5691d24c0fa0dcd9028d0cd2860692265db7a882af65fa3785aa
tags:
- latest
- 1.0.0
@@ -11,10 +11,10 @@ targets:
open-router:
source: OpenRouter API
sourceNamespace: open-router-chat-completions-api
- sourceRevisionDigest: sha256:cf24edf70629b35aad73aef00830d7d2f92f5f199bd6540c50f4887f848a33da
- sourceBlobDigest: sha256:8c7c009a8ce7e8466887920f6d8c5246378189395521adf6e1a0464a23cf2caa
+ sourceRevisionDigest: sha256:c597251b8315aeabdac8771a04fa223bbd27a7ee37c50e7865f78a63b009cd05
+ sourceBlobDigest: sha256:63d088f642ae5691d24c0fa0dcd9028d0cd2860692265db7a882af65fa3785aa
codeSamplesNamespace: open-router-python-code-samples
- codeSamplesRevisionDigest: sha256:e4432a5e58f6864b1422ab41cc380048fc21238fbad0baa17b07dd9d855bb8b7
+ codeSamplesRevisionDigest: sha256:a730ae87d313ee35964df1065a504ce678766c22dbc3b1692054285f0d381138
workflow:
workflowVersion: 1.0.0
speakeasyVersion: 1.787.0
diff --git a/RELEASES.md b/RELEASES.md
index 7db2a78a..8d0bde99 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -3159,4 +3159,14 @@ Based on:
### Generated
- [python v1.3.39] .
### Releases
-- [PyPI v1.3.39] https://pypi.org/project/openrouter/1.3.39 - .
\ No newline at end of file
+- [PyPI v1.3.39] https://pypi.org/project/openrouter/1.3.39 - .
+
+## 2026-10-09 15:16:59
+### Changes
+Based on:
+- OpenAPI Doc
+- Speakeasy CLI 1.787.0 (2.914.0) https://github.com/speakeasy-api/speakeasy
+### Generated
+- [python v1.3.40] .
+### Releases
+- [PyPI v1.3.40] https://pypi.org/project/openrouter/1.3.40 - .
\ No newline at end of file
diff --git a/docs/operations/updatekeysrequestbody.mdx b/docs/operations/updatekeysrequestbody.mdx
index 5db1aef1..9cf47d24 100644
--- a/docs/operations/updatekeysrequestbody.mdx
+++ b/docs/operations/updatekeysrequestbody.mdx
@@ -4,10 +4,11 @@ title: "UpdateKeysRequestBody"
## Fields
-| Field | Type | Required | Description | Example |
-| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `disabled` | *Optional[bool]* | ➖ | Whether to disable the API key | false |
-| `include_byok_in_limit` | *Optional[bool]* | ➖ | Whether to include BYOK usage in the limit | true |
-| `limit` | *OptionalNullable[float]* | ➖ | New spending limit for the API key in USD | 75 |
-| `limit_reset` | [OptionalNullable[operations.UpdateKeysLimitReset]](../operations/updatekeyslimitreset.mdx) | ➖ | New limit reset type for the API key (daily, weekly, monthly, or null for no reset). Resets happen automatically at midnight UTC, and weeks are Monday through Sunday. | daily |
-| `name` | *Optional[str]* | ➖ | New name for the API key | Updated API Key Name |
\ No newline at end of file
+| Field | Type | Required | Description | Example |
+| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `disabled` | *Optional[bool]* | ➖ | Whether to disable the API key | false |
+| `include_byok_in_limit` | *Optional[bool]* | ➖ | Whether to include BYOK usage in the limit | true |
+| `limit` | *OptionalNullable[float]* | ➖ | New spending limit for the API key in USD | 75 |
+| `limit_reset` | [OptionalNullable[operations.UpdateKeysLimitReset]](../operations/updatekeyslimitreset.mdx) | ➖ | New limit reset type for the API key (daily, weekly, monthly, or null for no reset). Resets happen automatically at midnight UTC, and weeks are Monday through Sunday. | daily |
+| `name` | *Optional[str]* | ➖ | New name for the API key | Updated API Key Name |
+| `workspace_id` | *Optional[str]* | ➖ | Move the API key to this workspace. The key keeps its value; guardrail selections move with it, while other workspace-scoped settings (presets, BYOK keys, broadcast destinations, routing rules) do not. Sending the key's current workspace is a no-op. | 0df9e665-d932-5740-b2c7-b52af166bc11 |
\ No newline at end of file
diff --git a/docs/sdks/apikeys/README.mdx b/docs/sdks/apikeys/README.mdx
index 07e3ac57..fb9a4aa0 100644
--- a/docs/sdks/apikeys/README.mdx
+++ b/docs/sdks/apikeys/README.mdx
@@ -274,8 +274,10 @@ with OpenRouter(
Update an existing API key. Authenticate with a [management key](/docs/guides/overview/auth/management-api-keys).
+Set `workspace_id` to move the key to another workspace. The key keeps its value, and any other fields in the same request are applied atomically with the move. Guardrail selections move with the key; other workspace-scoped settings (presets, BYOK keys, broadcast destinations, routing rules) do not. A move into or out of a HIPAA workspace is refused with `403`. In an organization, if the key's creator is not a member of a non-default target workspace, the request fails with `409`; add them to the workspace first.
+
\
-You can't change `workspace_id` through the API. The request body accepts only the fields listed below, and unrecognized fields are ignored. To move a key to another workspace, use the OpenRouter dashboard. If the body contains none of the accepted fields, the request fails with `400` and the message `No update fields provided`.
+The request body accepts only the fields listed below, and unrecognized fields are ignored. If the body contains none of the accepted fields, the request fails with `400` and the message `No update fields provided`.
### Example Usage: invalid_parameters
@@ -321,18 +323,19 @@ with OpenRouter(
### Parameters
-| Parameter | Type | Required | Description | Example |
-| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `hash` | *str* | ✅ | The hash identifier of the API key to update | f01d52606dc8f0a8303a7b5cc3fa07109c2e346cec7c0a16b40de462992ce943 |
-| `http_referer` | *Optional[str]* | ➖ | The app identifier should be your app's URL and is used as the primary identifier for rankings.
This is used to track API usage per application.
| |
-| `x_open_router_title` | *Optional[str]* | ➖ | The app display name allows you to customize how your app appears in OpenRouter's dashboard.
| |
-| `x_open_router_categories` | *Optional[str]* | ➖ | Comma-separated list of app categories (e.g. "cli-agent,cloud-agent"). Used for marketplace rankings.
| |
-| `disabled` | *Optional[bool]* | ➖ | Whether to disable the API key | false |
-| `include_byok_in_limit` | *Optional[bool]* | ➖ | Whether to include BYOK usage in the limit | true |
-| `limit` | *OptionalNullable[float]* | ➖ | New spending limit for the API key in USD | 75 |
-| `limit_reset` | [OptionalNullable[operations.UpdateKeysLimitReset]](../../operations/updatekeyslimitreset.mdx) | ➖ | New limit reset type for the API key (daily, weekly, monthly, or null for no reset). Resets happen automatically at midnight UTC, and weeks are Monday through Sunday. | daily |
-| `name` | *Optional[str]* | ➖ | New name for the API key | Updated API Key Name |
-| `retries` | [Optional[utils.RetryConfig]](../../models/utils/retryconfig.mdx) | ➖ | Configuration to override the default retry behavior of the client. | |
+| Parameter | Type | Required | Description | Example |
+| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `hash` | *str* | ✅ | The hash identifier of the API key to update | f01d52606dc8f0a8303a7b5cc3fa07109c2e346cec7c0a16b40de462992ce943 |
+| `http_referer` | *Optional[str]* | ➖ | The app identifier should be your app's URL and is used as the primary identifier for rankings.
This is used to track API usage per application.
| |
+| `x_open_router_title` | *Optional[str]* | ➖ | The app display name allows you to customize how your app appears in OpenRouter's dashboard.
| |
+| `x_open_router_categories` | *Optional[str]* | ➖ | Comma-separated list of app categories (e.g. "cli-agent,cloud-agent"). Used for marketplace rankings.
| |
+| `disabled` | *Optional[bool]* | ➖ | Whether to disable the API key | false |
+| `include_byok_in_limit` | *Optional[bool]* | ➖ | Whether to include BYOK usage in the limit | true |
+| `limit` | *OptionalNullable[float]* | ➖ | New spending limit for the API key in USD | 75 |
+| `limit_reset` | [OptionalNullable[operations.UpdateKeysLimitReset]](../../operations/updatekeyslimitreset.mdx) | ➖ | New limit reset type for the API key (daily, weekly, monthly, or null for no reset). Resets happen automatically at midnight UTC, and weeks are Monday through Sunday. | daily |
+| `name` | *Optional[str]* | ➖ | New name for the API key | Updated API Key Name |
+| `workspace_id` | *Optional[str]* | ➖ | Move the API key to this workspace. The key keeps its value; guardrail selections move with it, while other workspace-scoped settings (presets, BYOK keys, broadcast destinations, routing rules) do not. Sending the key's current workspace is a no-op. | 0df9e665-d932-5740-b2c7-b52af166bc11 |
+| `retries` | [Optional[utils.RetryConfig]](../../models/utils/retryconfig.mdx) | ➖ | Configuration to override the default retry behavior of the client. | |
### Response
@@ -344,7 +347,9 @@ with OpenRouter(
| ----------------------------------- | ----------------------------------- | ----------------------------------- |
| errors.BadRequestResponseError | 400 | application/json |
| errors.UnauthorizedResponseError | 401 | application/json |
+| errors.ForbiddenResponseError | 403 | application/json |
| errors.NotFoundResponseError | 404 | application/json |
+| errors.ConflictResponseError | 409 | application/json |
| errors.TooManyRequestsResponseError | 429 | application/json |
| errors.InternalServerResponseError | 500 | application/json |
| errors.OpenRouterDefaultError | 4XX, 5XX | \*/\* |
\ No newline at end of file
diff --git a/pyproject.toml b/pyproject.toml
index 603917d3..58dec50a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openrouter"
-version = "1.3.39"
+version = "1.3.40"
description = "Official Python Client SDK for OpenRouter."
authors = [{ name = "OpenRouter" },]
readme = "README-PYPI.md"
diff --git a/src/openrouter/_version.py b/src/openrouter/_version.py
index 0cf6cc22..b42d7fe1 100644
--- a/src/openrouter/_version.py
+++ b/src/openrouter/_version.py
@@ -3,10 +3,10 @@
import importlib.metadata
__title__: str = "openrouter"
-__version__: str = "1.3.39"
+__version__: str = "1.3.40"
__openapi_doc_version__: str = "1.0.0"
__gen_version__: str = "2.914.0"
-__user_agent__: str = "speakeasy-sdk/python 1.3.39 2.914.0 1.0.0 openrouter"
+__user_agent__: str = "speakeasy-sdk/python 1.3.40 2.914.0 1.0.0 openrouter"
try:
if __package__ is not None:
diff --git a/src/openrouter/api_keys.py b/src/openrouter/api_keys.py
index 1b9ba98c..3c95e38b 100644
--- a/src/openrouter/api_keys.py
+++ b/src/openrouter/api_keys.py
@@ -1425,6 +1425,7 @@ def update(
limit: OptionalNullable[float] = UNSET,
limit_reset: OptionalNullable[operations.UpdateKeysLimitReset] = UNSET,
name: Optional[str] = None,
+ workspace_id: Optional[str] = None,
retries: OptionalNullable[utils.RetryConfig] = UNSET,
server_url: Optional[str] = None,
timeout_ms: Optional[int] = None,
@@ -1434,8 +1435,10 @@ def update(
Update an existing API key. Authenticate with a [management key](/docs/guides/overview/auth/management-api-keys).
+ Set `workspace_id` to move the key to another workspace. The key keeps its value, and any other fields in the same request are applied atomically with the move. Guardrail selections move with the key; other workspace-scoped settings (presets, BYOK keys, broadcast destinations, routing rules) do not. A move into or out of a HIPAA workspace is refused with `403`. In an organization, if the key's creator is not a member of a non-default target workspace, the request fails with `409`; add them to the workspace first.
+
- You can't change `workspace_id` through the API. The request body accepts only the fields listed below, and unrecognized fields are ignored. To move a key to another workspace, use the OpenRouter dashboard. If the body contains none of the accepted fields, the request fails with `400` and the message `No update fields provided`.
+ The request body accepts only the fields listed below, and unrecognized fields are ignored. If the body contains none of the accepted fields, the request fails with `400` and the message `No update fields provided`.
:param hash: The hash identifier of the API key to update
@@ -1451,6 +1454,7 @@ def update(
:param limit: New spending limit for the API key in USD
:param limit_reset: New limit reset type for the API key (daily, weekly, monthly, or null for no reset). Resets happen automatically at midnight UTC, and weeks are Monday through Sunday.
:param name: New name for the API key
+ :param workspace_id: Move the API key to this workspace. The key keeps its value; guardrail selections move with it, while other workspace-scoped settings (presets, BYOK keys, broadcast destinations, routing rules) do not. Sending the key's current workspace is a no-op.
:param retries: Override the default retry configuration for this method
:param server_url: Override the default server URL for this method
:param timeout_ms: Override the default request timeout configuration for this method in milliseconds
@@ -1477,6 +1481,7 @@ def update(
limit=limit,
limit_reset=limit_reset,
name=name,
+ workspace_id=workspace_id,
),
)
@@ -1551,11 +1556,21 @@ def update(
errors.UnauthorizedResponseErrorData, http_res
)
raise errors.UnauthorizedResponseError(response_data, http_res)
+ if utils.match_response(http_res, "403", "application/json"):
+ response_data = unmarshal_json_response(
+ errors.ForbiddenResponseErrorData, http_res
+ )
+ raise errors.ForbiddenResponseError(response_data, http_res)
if utils.match_response(http_res, "404", "application/json"):
response_data = unmarshal_json_response(
errors.NotFoundResponseErrorData, http_res
)
raise errors.NotFoundResponseError(response_data, http_res)
+ if utils.match_response(http_res, "409", "application/json"):
+ response_data = unmarshal_json_response(
+ errors.ConflictResponseErrorData, http_res
+ )
+ raise errors.ConflictResponseError(response_data, http_res)
if utils.match_response(http_res, "429", "application/json"):
response_data = unmarshal_json_response(
errors.TooManyRequestsResponseErrorData, http_res
@@ -1591,6 +1606,7 @@ async def update_async(
limit: OptionalNullable[float] = UNSET,
limit_reset: OptionalNullable[operations.UpdateKeysLimitReset] = UNSET,
name: Optional[str] = None,
+ workspace_id: Optional[str] = None,
retries: OptionalNullable[utils.RetryConfig] = UNSET,
server_url: Optional[str] = None,
timeout_ms: Optional[int] = None,
@@ -1600,8 +1616,10 @@ async def update_async(
Update an existing API key. Authenticate with a [management key](/docs/guides/overview/auth/management-api-keys).
+ Set `workspace_id` to move the key to another workspace. The key keeps its value, and any other fields in the same request are applied atomically with the move. Guardrail selections move with the key; other workspace-scoped settings (presets, BYOK keys, broadcast destinations, routing rules) do not. A move into or out of a HIPAA workspace is refused with `403`. In an organization, if the key's creator is not a member of a non-default target workspace, the request fails with `409`; add them to the workspace first.
+
- You can't change `workspace_id` through the API. The request body accepts only the fields listed below, and unrecognized fields are ignored. To move a key to another workspace, use the OpenRouter dashboard. If the body contains none of the accepted fields, the request fails with `400` and the message `No update fields provided`.
+ The request body accepts only the fields listed below, and unrecognized fields are ignored. If the body contains none of the accepted fields, the request fails with `400` and the message `No update fields provided`.
:param hash: The hash identifier of the API key to update
@@ -1617,6 +1635,7 @@ async def update_async(
:param limit: New spending limit for the API key in USD
:param limit_reset: New limit reset type for the API key (daily, weekly, monthly, or null for no reset). Resets happen automatically at midnight UTC, and weeks are Monday through Sunday.
:param name: New name for the API key
+ :param workspace_id: Move the API key to this workspace. The key keeps its value; guardrail selections move with it, while other workspace-scoped settings (presets, BYOK keys, broadcast destinations, routing rules) do not. Sending the key's current workspace is a no-op.
:param retries: Override the default retry configuration for this method
:param server_url: Override the default server URL for this method
:param timeout_ms: Override the default request timeout configuration for this method in milliseconds
@@ -1643,6 +1662,7 @@ async def update_async(
limit=limit,
limit_reset=limit_reset,
name=name,
+ workspace_id=workspace_id,
),
)
@@ -1717,11 +1737,21 @@ async def update_async(
errors.UnauthorizedResponseErrorData, http_res
)
raise errors.UnauthorizedResponseError(response_data, http_res)
+ if utils.match_response(http_res, "403", "application/json"):
+ response_data = unmarshal_json_response(
+ errors.ForbiddenResponseErrorData, http_res
+ )
+ raise errors.ForbiddenResponseError(response_data, http_res)
if utils.match_response(http_res, "404", "application/json"):
response_data = unmarshal_json_response(
errors.NotFoundResponseErrorData, http_res
)
raise errors.NotFoundResponseError(response_data, http_res)
+ if utils.match_response(http_res, "409", "application/json"):
+ response_data = unmarshal_json_response(
+ errors.ConflictResponseErrorData, http_res
+ )
+ raise errors.ConflictResponseError(response_data, http_res)
if utils.match_response(http_res, "429", "application/json"):
response_data = unmarshal_json_response(
errors.TooManyRequestsResponseErrorData, http_res
diff --git a/src/openrouter/operations/updatekeys.py b/src/openrouter/operations/updatekeys.py
index 3862d925..e9ed9da0 100644
--- a/src/openrouter/operations/updatekeys.py
+++ b/src/openrouter/operations/updatekeys.py
@@ -108,6 +108,8 @@ class UpdateKeysRequestBodyTypedDict(TypedDict):
r"""New limit reset type for the API key (daily, weekly, monthly, or null for no reset). Resets happen automatically at midnight UTC, and weeks are Monday through Sunday."""
name: NotRequired[str]
r"""New name for the API key"""
+ workspace_id: NotRequired[str]
+ r"""Move the API key to this workspace. The key keeps its value; guardrail selections move with it, while other workspace-scoped settings (presets, BYOK keys, broadcast destinations, routing rules) do not. Sending the key's current workspace is a no-op."""
class UpdateKeysRequestBody(BaseModel):
@@ -126,10 +128,20 @@ class UpdateKeysRequestBody(BaseModel):
name: Optional[str] = None
r"""New name for the API key"""
+ workspace_id: Optional[str] = None
+ r"""Move the API key to this workspace. The key keeps its value; guardrail selections move with it, while other workspace-scoped settings (presets, BYOK keys, broadcast destinations, routing rules) do not. Sending the key's current workspace is a no-op."""
+
@model_serializer(mode="wrap")
def serialize_model(self, handler):
optional_fields = set(
- ["disabled", "include_byok_in_limit", "limit", "limit_reset", "name"]
+ [
+ "disabled",
+ "include_byok_in_limit",
+ "limit",
+ "limit_reset",
+ "name",
+ "workspace_id",
+ ]
)
nullable_fields = set(["limit", "limit_reset"])
serialized = handler(self)
diff --git a/uv.lock b/uv.lock
index aa7019ff..0cff5aa5 100644
--- a/uv.lock
+++ b/uv.lock
@@ -213,7 +213,7 @@ wheels = [
[[package]]
name = "openrouter"
-version = "1.3.39"
+version = "1.3.40"
source = { editable = "." }
dependencies = [
{ name = "httpcore" },