From 2876c0ae6871a0bb6bfa22e45b8100a5583ecbd9 Mon Sep 17 00:00:00 2001 From: "workos-sdk-automation[bot]" <255426317+workos-sdk-automation[bot]@users.noreply.github.com> Date: Wed, 14 Oct 2026 21:31:32 +0000 Subject: [PATCH 1/8] feat(pipes)!: Change Pipes API surface --- ..._authorize_url_request_connection_owner.py | 33 +++++++++++++++++++ .../models/data_integration_ownership.py | 8 +++-- ..._authorize_url_request_connection_owner.py | 6 ++-- ...get_user_token_request_connection_owner.py | 6 ++-- ..._credentials_request_2_connection_owner.py | 12 +++++++ ...nt_credentials_request_connection_owner.py | 6 ++-- ...nd_credentials_request_connection_owner.py | 6 ++-- ..._authorize_url_request_connection_owner.py | 12 +++++++ ...nt_credentials_request_connection_owner.py | 12 +++++++ ...ata_integration_authorize_url_request.json | 11 +++++++ ...data_integration_credentials_response.json | 8 ++++- .../data_integration_vended_credential.json | 17 ++++++++++ ...ata_integration_authorize_url_request.json | 1 + ...ons_upsert_client_credentials_request.json | 3 +- ...s_upsert_client_credentials_request_2.json | 13 ++++++++ ...ata_integration_authorize_url_request.json | 12 +++++++ ...ons_upsert_client_credentials_request.json | 14 ++++++++ 17 files changed, 168 insertions(+), 12 deletions(-) create mode 100644 src/workos/common/models/add_data_integrations_get_data_integration_authorize_url_request_connection_owner.py create mode 100644 src/workos/common/models/data_integrations_upsert_client_credentials_request_2_connection_owner.py create mode 100644 src/workos/common/models/reauthorize_data_integrations_get_data_integration_authorize_url_request_connection_owner.py create mode 100644 src/workos/common/models/reauthorize_data_integrations_upsert_client_credentials_request_connection_owner.py create mode 100644 tests/fixtures/add_data_integrations_get_data_integration_authorize_url_request.json create mode 100644 tests/fixtures/data_integration_vended_credential.json create mode 100644 tests/fixtures/data_integrations_upsert_client_credentials_request_2.json create mode 100644 tests/fixtures/reauthorize_data_integrations_get_data_integration_authorize_url_request.json create mode 100644 tests/fixtures/reauthorize_data_integrations_upsert_client_credentials_request.json diff --git a/src/workos/common/models/add_data_integrations_get_data_integration_authorize_url_request_connection_owner.py b/src/workos/common/models/add_data_integrations_get_data_integration_authorize_url_request_connection_owner.py new file mode 100644 index 00000000..32246b07 --- /dev/null +++ b/src/workos/common/models/add_data_integrations_get_data_integration_authorize_url_request_connection_owner.py @@ -0,0 +1,33 @@ +# This file is auto-generated by oagen. Do not edit. + +"""Enumeration of add data integrations get data integration authorize url request connection owner values.""" + +from __future__ import annotations + +from enum import Enum +from typing import Literal, TypeAlias + + +class AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner( + str, Enum +): + """Known values for AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner.""" + + USER = "user" + ORGANIZATION = "organization" + + @classmethod + def _missing_( + cls, value: object + ) -> AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner | None: + if not isinstance(value, str): + return None + unknown = str.__new__(cls, value) + unknown._name_ = value.upper() + unknown._value_ = value + return unknown + + +AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwnerLiteral: TypeAlias = Literal[ + "user", "organization" +] diff --git a/src/workos/common/models/data_integration_ownership.py b/src/workos/common/models/data_integration_ownership.py index 98aac778..73f0e695 100644 --- a/src/workos/common/models/data_integration_ownership.py +++ b/src/workos/common/models/data_integration_ownership.py @@ -2,7 +2,11 @@ from typing import TypeAlias -from .create_data_integration_ownership import CreateDataIntegrationOwnership +from .add_data_integrations_get_data_integration_authorize_url_request_connection_owner import ( + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner, +) -DataIntegrationOwnership: TypeAlias = CreateDataIntegrationOwnership +DataIntegrationOwnership: TypeAlias = ( + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner +) __all__ = ["DataIntegrationOwnership"] diff --git a/src/workos/common/models/data_integrations_get_data_integration_authorize_url_request_connection_owner.py b/src/workos/common/models/data_integrations_get_data_integration_authorize_url_request_connection_owner.py index f114ebc0..f11e4b16 100644 --- a/src/workos/common/models/data_integrations_get_data_integration_authorize_url_request_connection_owner.py +++ b/src/workos/common/models/data_integrations_get_data_integration_authorize_url_request_connection_owner.py @@ -2,9 +2,11 @@ from typing import TypeAlias -from .create_data_integration_ownership import CreateDataIntegrationOwnership +from .add_data_integrations_get_data_integration_authorize_url_request_connection_owner import ( + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner, +) DataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner: TypeAlias = ( - CreateDataIntegrationOwnership + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner ) __all__ = ["DataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner"] diff --git a/src/workos/common/models/data_integrations_get_user_token_request_connection_owner.py b/src/workos/common/models/data_integrations_get_user_token_request_connection_owner.py index 49047c49..41c0a0a6 100644 --- a/src/workos/common/models/data_integrations_get_user_token_request_connection_owner.py +++ b/src/workos/common/models/data_integrations_get_user_token_request_connection_owner.py @@ -2,9 +2,11 @@ from typing import TypeAlias -from .create_data_integration_ownership import CreateDataIntegrationOwnership +from .add_data_integrations_get_data_integration_authorize_url_request_connection_owner import ( + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner, +) DataIntegrationsGetUserTokenRequestConnectionOwner: TypeAlias = ( - CreateDataIntegrationOwnership + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner ) __all__ = ["DataIntegrationsGetUserTokenRequestConnectionOwner"] diff --git a/src/workos/common/models/data_integrations_upsert_client_credentials_request_2_connection_owner.py b/src/workos/common/models/data_integrations_upsert_client_credentials_request_2_connection_owner.py new file mode 100644 index 00000000..84b552ef --- /dev/null +++ b/src/workos/common/models/data_integrations_upsert_client_credentials_request_2_connection_owner.py @@ -0,0 +1,12 @@ +# This file is auto-generated by oagen. Do not edit. + +from typing import TypeAlias + +from .add_data_integrations_get_data_integration_authorize_url_request_connection_owner import ( + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner, +) + +DataIntegrationsUpsertClientCredentialsRequest2ConnectionOwner: TypeAlias = ( + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner +) +__all__ = ["DataIntegrationsUpsertClientCredentialsRequest2ConnectionOwner"] diff --git a/src/workos/common/models/data_integrations_upsert_client_credentials_request_connection_owner.py b/src/workos/common/models/data_integrations_upsert_client_credentials_request_connection_owner.py index f67f8275..f01e23bc 100644 --- a/src/workos/common/models/data_integrations_upsert_client_credentials_request_connection_owner.py +++ b/src/workos/common/models/data_integrations_upsert_client_credentials_request_connection_owner.py @@ -2,9 +2,11 @@ from typing import TypeAlias -from .create_data_integration_ownership import CreateDataIntegrationOwnership +from .add_data_integrations_get_data_integration_authorize_url_request_connection_owner import ( + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner, +) DataIntegrationsUpsertClientCredentialsRequestConnectionOwner: TypeAlias = ( - CreateDataIntegrationOwnership + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner ) __all__ = ["DataIntegrationsUpsertClientCredentialsRequestConnectionOwner"] diff --git a/src/workos/common/models/data_integrations_vend_credentials_request_connection_owner.py b/src/workos/common/models/data_integrations_vend_credentials_request_connection_owner.py index 6569cbfb..3ecf2872 100644 --- a/src/workos/common/models/data_integrations_vend_credentials_request_connection_owner.py +++ b/src/workos/common/models/data_integrations_vend_credentials_request_connection_owner.py @@ -2,9 +2,11 @@ from typing import TypeAlias -from .create_data_integration_ownership import CreateDataIntegrationOwnership +from .add_data_integrations_get_data_integration_authorize_url_request_connection_owner import ( + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner, +) DataIntegrationsVendCredentialsRequestConnectionOwner: TypeAlias = ( - CreateDataIntegrationOwnership + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner ) __all__ = ["DataIntegrationsVendCredentialsRequestConnectionOwner"] diff --git a/src/workos/common/models/reauthorize_data_integrations_get_data_integration_authorize_url_request_connection_owner.py b/src/workos/common/models/reauthorize_data_integrations_get_data_integration_authorize_url_request_connection_owner.py new file mode 100644 index 00000000..9b9607d8 --- /dev/null +++ b/src/workos/common/models/reauthorize_data_integrations_get_data_integration_authorize_url_request_connection_owner.py @@ -0,0 +1,12 @@ +# This file is auto-generated by oagen. Do not edit. + +from typing import TypeAlias + +from .add_data_integrations_get_data_integration_authorize_url_request_connection_owner import ( + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner, +) + +ReauthorizeDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner: TypeAlias = AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner +__all__ = [ + "ReauthorizeDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner" +] diff --git a/src/workos/common/models/reauthorize_data_integrations_upsert_client_credentials_request_connection_owner.py b/src/workos/common/models/reauthorize_data_integrations_upsert_client_credentials_request_connection_owner.py new file mode 100644 index 00000000..c38e0a23 --- /dev/null +++ b/src/workos/common/models/reauthorize_data_integrations_upsert_client_credentials_request_connection_owner.py @@ -0,0 +1,12 @@ +# This file is auto-generated by oagen. Do not edit. + +from typing import TypeAlias + +from .add_data_integrations_get_data_integration_authorize_url_request_connection_owner import ( + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner, +) + +ReauthorizeDataIntegrationsUpsertClientCredentialsRequestConnectionOwner: TypeAlias = ( + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner +) +__all__ = ["ReauthorizeDataIntegrationsUpsertClientCredentialsRequestConnectionOwner"] diff --git a/tests/fixtures/add_data_integrations_get_data_integration_authorize_url_request.json b/tests/fixtures/add_data_integrations_get_data_integration_authorize_url_request.json new file mode 100644 index 00000000..58609a5d --- /dev/null +++ b/tests/fixtures/add_data_integrations_get_data_integration_authorize_url_request.json @@ -0,0 +1,11 @@ +{ + "account_display_name": "Acme production", + "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + "connection_owner": "user", + "return_to": "https://example.com/callback", + "config": { + "subdomain": "acme" + }, + "connection_intent": "add" +} diff --git a/tests/fixtures/data_integration_credentials_response.json b/tests/fixtures/data_integration_credentials_response.json index 9efedd6d..bbac768e 100644 --- a/tests/fixtures/data_integration_credentials_response.json +++ b/tests/fixtures/data_integration_credentials_response.json @@ -9,7 +9,13 @@ "repo", "user:email" ], - "missing_scopes": [] + "missing_scopes": [], + "config": { + "account": "myorg-myaccount" + }, + "metadata": { + "instance_url": "https://acme.my.salesforce.com" + } }, "error": "not_installed" } diff --git a/tests/fixtures/data_integration_vended_credential.json b/tests/fixtures/data_integration_vended_credential.json new file mode 100644 index 00000000..c9958827 --- /dev/null +++ b/tests/fixtures/data_integration_vended_credential.json @@ -0,0 +1,17 @@ +{ + "object": "credential", + "auth_method": "oauth", + "value": "gho_16C7e42F292c6912E7710c838347Ae178B4a", + "expires_at": "2025-12-31T23:59:59.000Z", + "scopes": [ + "repo", + "user:email" + ], + "missing_scopes": [], + "config": { + "account": "myorg-myaccount" + }, + "metadata": { + "instance_url": "https://acme.my.salesforce.com" + } +} diff --git a/tests/fixtures/data_integrations_get_data_integration_authorize_url_request.json b/tests/fixtures/data_integrations_get_data_integration_authorize_url_request.json index 4a7ea84c..36bc1662 100644 --- a/tests/fixtures/data_integrations_get_data_integration_authorize_url_request.json +++ b/tests/fixtures/data_integrations_get_data_integration_authorize_url_request.json @@ -1,4 +1,5 @@ { + "account_display_name": "Acme production", "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", "connection_owner": "user", diff --git a/tests/fixtures/data_integrations_upsert_client_credentials_request.json b/tests/fixtures/data_integrations_upsert_client_credentials_request.json index 9497d49c..4e38b927 100644 --- a/tests/fixtures/data_integrations_upsert_client_credentials_request.json +++ b/tests/fixtures/data_integrations_upsert_client_credentials_request.json @@ -1,7 +1,8 @@ { + "account_display_name": "Acme production", + "account_identifier": "workspace_123", "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", - "connected_account_id": "data_installation_01EHZNVPK3SFK441A1RGBFSHRT", "connection_owner": "user", "client_id": "3MVG9...", "client_secret": "shhh-secret", diff --git a/tests/fixtures/data_integrations_upsert_client_credentials_request_2.json b/tests/fixtures/data_integrations_upsert_client_credentials_request_2.json new file mode 100644 index 00000000..a8baf034 --- /dev/null +++ b/tests/fixtures/data_integrations_upsert_client_credentials_request_2.json @@ -0,0 +1,13 @@ +{ + "account_display_name": "Acme production", + "account_identifier": "workspace_123", + "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + "connection_owner": "user", + "client_id": "3MVG9...", + "client_secret": "shhh-secret", + "config": { + "salesforce_host": "acme.my.salesforce.com" + }, + "connected_account_id": "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" +} diff --git a/tests/fixtures/reauthorize_data_integrations_get_data_integration_authorize_url_request.json b/tests/fixtures/reauthorize_data_integrations_get_data_integration_authorize_url_request.json new file mode 100644 index 00000000..b8737b4f --- /dev/null +++ b/tests/fixtures/reauthorize_data_integrations_get_data_integration_authorize_url_request.json @@ -0,0 +1,12 @@ +{ + "account_display_name": "Acme production", + "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + "connection_owner": "user", + "return_to": "https://example.com/callback", + "config": { + "subdomain": "acme" + }, + "connection_intent": "reauthorize", + "connected_account_id": "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" +} diff --git a/tests/fixtures/reauthorize_data_integrations_upsert_client_credentials_request.json b/tests/fixtures/reauthorize_data_integrations_upsert_client_credentials_request.json new file mode 100644 index 00000000..1387f567 --- /dev/null +++ b/tests/fixtures/reauthorize_data_integrations_upsert_client_credentials_request.json @@ -0,0 +1,14 @@ +{ + "account_display_name": "Acme production", + "account_identifier": "workspace_123", + "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + "connection_owner": "user", + "client_id": "3MVG9...", + "client_secret": "shhh-secret", + "config": { + "salesforce_host": "acme.my.salesforce.com" + }, + "connection_intent": "reauthorize", + "connected_account_id": "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" +} From f8d0fead78373382730d54917c43ec9b4bfaf5d1 Mon Sep 17 00:00:00 2001 From: "workos-sdk-automation[bot]" <255426317+workos-sdk-automation[bot]@users.noreply.github.com> Date: Wed, 14 Oct 2026 21:31:32 +0000 Subject: [PATCH 2/8] feat(admin_portal): Update admin portal API surface --- src/workos/admin_portal/_resource.py | 74 +++++++++++++++++++ src/workos/admin_portal/models/__init__.py | 1 + .../admin_portal/models/generate_link.py | 5 ++ src/workos/admin_portal/models/revoke_link.py | 36 +++++++++ .../models/agent_blueprints_create_request.py | 12 +++ ..._blueprints_create_request_invocable_as.py | 47 ++++++++++++ .../models/agent_blueprint_created_data.py | 9 +++ ...ent_blueprint_created_data_invocable_as.py | 43 +++++++++++ ...reated_data_invocable_as_instance_types.py | 31 ++++++++ ...ate_request_invocable_as_instance_types.py | 12 +++ .../agent_instance_created_data_type.py | 31 ++------ .../create_data_integration_ownership.py | 31 ++------ ...key_connection_request_connection_owner.py | 12 +++ tests/fixtures/agent_blueprint_created.json | 6 ++ .../agent_blueprint_created_data.json | 6 ++ ...t_blueprint_created_data_invocable_as.json | 6 ++ .../agent_blueprints_create_request.json | 6 ++ ...lueprints_create_request_invocable_as.json | 6 ++ ...ons_create_api_key_connection_request.json | 9 +++ tests/fixtures/generate_link.json | 1 + tests/fixtures/revoke_link.json | 4 + tests/test_admin_portal.py | 23 +++++- tests/test_admin_portal_models_round_trip.py | 2 +- 23 files changed, 364 insertions(+), 49 deletions(-) create mode 100644 src/workos/admin_portal/models/revoke_link.py create mode 100644 src/workos/agents/models/agent_blueprints_create_request_invocable_as.py create mode 100644 src/workos/common/models/agent_blueprint_created_data_invocable_as.py create mode 100644 src/workos/common/models/agent_blueprint_created_data_invocable_as_instance_types.py create mode 100644 src/workos/common/models/agent_blueprints_create_request_invocable_as_instance_types.py create mode 100644 src/workos/common/models/data_integrations_create_api_key_connection_request_connection_owner.py create mode 100644 tests/fixtures/agent_blueprint_created_data_invocable_as.json create mode 100644 tests/fixtures/agent_blueprints_create_request_invocable_as.json create mode 100644 tests/fixtures/data_integrations_create_api_key_connection_request.json create mode 100644 tests/fixtures/revoke_link.json diff --git a/src/workos/admin_portal/_resource.py b/src/workos/admin_portal/_resource.py index 79c09348..cb85a3ce 100644 --- a/src/workos/admin_portal/_resource.py +++ b/src/workos/admin_portal/_resource.py @@ -25,6 +25,7 @@ def generate_link( organization: str, return_url: str | None = None, success_url: str | None = None, + external_user_id: str | None = None, intent: GenerateLinkIntent | str | None = None, it_contact_emails: list[str] | None = None, request_options: RequestOptions | None = None, @@ -37,6 +38,7 @@ def generate_link( return_url: The URL to go to when an admin clicks on your logo in the Admin Portal. If not specified, the return URL configured on the [Admin Portal](https://dashboard.workos.com/admin-portal) page will be used. success_url: The URL to redirect the admin to when they finish setup. If not specified, the success URL configured on the [Admin Portal](https://dashboard.workos.com/admin-portal) page will be used. organization: An [Organization](https://workos.com/docs/reference/organization) identifier. + external_user_id: The ID of the user in your application the link is generated for. The link and any Admin Portal sessions started from it can later be revoked by this ID, for example when that user is deprovisioned from your application. intent: The intent of the Admin Portal. - `sso` - Launch Admin Portal for creating SSO connections - `dsync` - Launch Admin Portal for creating Directory Sync connections @@ -66,6 +68,7 @@ def generate_link( "return_url": return_url, "success_url": success_url, "organization": organization, + "external_user_id": external_user_id, "intent": enum_value(intent) if intent is not None else None, "it_contact_emails": it_contact_emails, }.items() @@ -79,6 +82,40 @@ def generate_link( request_options=request_options, ) + def create_revoke_link( + self, + *, + organization: str, + external_user_id: str, + request_options: RequestOptions | None = None, + ) -> None: + """Revoke Portal Links + + Revokes every Portal Link generated for the given organization with the given `external_user_id`, and immediately ends any Admin Portal sessions that were started from those links. Use this when an admin is deprovisioned from your application: unopened links stop working, and their already-open portal sessions are ended. Only links that were generated with an `external_user_id` can be revoked, so pass one to `generate_link` for every admin you may later need to deprovision. + + Args: + organization: An [Organization](https://workos.com/docs/reference/organization) identifier. + external_user_id: The `external_user_id` the Portal Links were generated with for this organization. Every matching link is revoked, along with any Admin Portal sessions started from those links. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Raises: + NotFoundError: If the resource is not found (404). + UnprocessableEntityError: If the request data is unprocessable (422). + AuthenticationError: If the API key is invalid (401). + RateLimitExceededError: If rate limited (429). + ServerError: If the server returns a 5xx error. + """ + body: dict[str, Any] = { + "organization": organization, + "external_user_id": external_user_id, + } + self._client.request( + method="post", + path=("portal", "revoke_link"), + body=body, + request_options=request_options, + ) + class AsyncAdminPortal: """Admin Portal API resources (async).""" @@ -92,6 +129,7 @@ async def generate_link( organization: str, return_url: str | None = None, success_url: str | None = None, + external_user_id: str | None = None, intent: GenerateLinkIntent | str | None = None, it_contact_emails: list[str] | None = None, request_options: RequestOptions | None = None, @@ -104,6 +142,7 @@ async def generate_link( return_url: The URL to go to when an admin clicks on your logo in the Admin Portal. If not specified, the return URL configured on the [Admin Portal](https://dashboard.workos.com/admin-portal) page will be used. success_url: The URL to redirect the admin to when they finish setup. If not specified, the success URL configured on the [Admin Portal](https://dashboard.workos.com/admin-portal) page will be used. organization: An [Organization](https://workos.com/docs/reference/organization) identifier. + external_user_id: The ID of the user in your application the link is generated for. The link and any Admin Portal sessions started from it can later be revoked by this ID, for example when that user is deprovisioned from your application. intent: The intent of the Admin Portal. - `sso` - Launch Admin Portal for creating SSO connections - `dsync` - Launch Admin Portal for creating Directory Sync connections @@ -133,6 +172,7 @@ async def generate_link( "return_url": return_url, "success_url": success_url, "organization": organization, + "external_user_id": external_user_id, "intent": enum_value(intent) if intent is not None else None, "it_contact_emails": it_contact_emails, }.items() @@ -145,3 +185,37 @@ async def generate_link( model=PortalLinkResponse, request_options=request_options, ) + + async def create_revoke_link( + self, + *, + organization: str, + external_user_id: str, + request_options: RequestOptions | None = None, + ) -> None: + """Revoke Portal Links + + Revokes every Portal Link generated for the given organization with the given `external_user_id`, and immediately ends any Admin Portal sessions that were started from those links. Use this when an admin is deprovisioned from your application: unopened links stop working, and their already-open portal sessions are ended. Only links that were generated with an `external_user_id` can be revoked, so pass one to `generate_link` for every admin you may later need to deprovision. + + Args: + organization: An [Organization](https://workos.com/docs/reference/organization) identifier. + external_user_id: The `external_user_id` the Portal Links were generated with for this organization. Every matching link is revoked, along with any Admin Portal sessions started from those links. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Raises: + NotFoundError: If the resource is not found (404). + UnprocessableEntityError: If the request data is unprocessable (422). + AuthenticationError: If the API key is invalid (401). + RateLimitExceededError: If rate limited (429). + ServerError: If the server returns a 5xx error. + """ + body: dict[str, Any] = { + "organization": organization, + "external_user_id": external_user_id, + } + await self._client.request( + method="post", + path=("portal", "revoke_link"), + body=body, + request_options=request_options, + ) diff --git a/src/workos/admin_portal/models/__init__.py b/src/workos/admin_portal/models/__init__.py index fa9b493e..2ac9b35c 100644 --- a/src/workos/admin_portal/models/__init__.py +++ b/src/workos/admin_portal/models/__init__.py @@ -2,3 +2,4 @@ from .generate_link import GenerateLink as GenerateLink from .portal_link_response import PortalLinkResponse as PortalLinkResponse +from .revoke_link import RevokeLink as RevokeLink diff --git a/src/workos/admin_portal/models/generate_link.py b/src/workos/admin_portal/models/generate_link.py index e02c4881..69bd1126 100644 --- a/src/workos/admin_portal/models/generate_link.py +++ b/src/workos/admin_portal/models/generate_link.py @@ -20,6 +20,8 @@ class GenerateLink: """The URL to go to when an admin clicks on your logo in the Admin Portal. If not specified, the return URL configured on the [Admin Portal](https://dashboard.workos.com/admin-portal) page will be used.""" success_url: str | None = None """The URL to redirect the admin to when they finish setup. If not specified, the success URL configured on the [Admin Portal](https://dashboard.workos.com/admin-portal) page will be used.""" + external_user_id: str | None = None + """The ID of the user in your application the link is generated for. The link and any Admin Portal sessions started from it can later be revoked by this ID, for example when that user is deprovisioned from your application.""" intent: GenerateLinkIntent | None = None """ The intent of the Admin Portal. @@ -41,6 +43,7 @@ def from_dict(cls, data: dict[str, Any]) -> GenerateLink: organization=data["organization"], return_url=data.get("return_url"), success_url=data.get("success_url"), + external_user_id=data.get("external_user_id"), intent=GenerateLinkIntent(_v_intent) if (_v_intent := data.get("intent")) is not None else None, @@ -57,6 +60,8 @@ def to_dict(self) -> dict[str, Any]: result["return_url"] = self.return_url if self.success_url is not None: result["success_url"] = self.success_url + if self.external_user_id is not None: + result["external_user_id"] = self.external_user_id if self.intent is not None: result["intent"] = ( self.intent.value if isinstance(self.intent, Enum) else self.intent diff --git a/src/workos/admin_portal/models/revoke_link.py b/src/workos/admin_portal/models/revoke_link.py new file mode 100644 index 00000000..b62e131d --- /dev/null +++ b/src/workos/admin_portal/models/revoke_link.py @@ -0,0 +1,36 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any + +from workos._types import _raise_deserialize_error + + +@dataclass(slots=True) +class RevokeLink: + """Revoke Link model.""" + + organization: str + """An [Organization](https://workos.com/docs/reference/organization) identifier.""" + external_user_id: str + """The `external_user_id` the Portal Links were generated with for this organization. Every matching link is revoked, along with any Admin Portal sessions started from those links.""" + + @classmethod + def from_dict(cls, data: dict[str, Any]) -> RevokeLink: + """Deserialize from a dictionary.""" + try: + return cls( + organization=data["organization"], + external_user_id=data["external_user_id"], + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error("RevokeLink", e) + + def to_dict(self) -> dict[str, Any]: + """Serialize to a dictionary.""" + result: dict[str, Any] = {} + result["organization"] = self.organization + result["external_user_id"] = self.external_user_id + return result diff --git a/src/workos/agents/models/agent_blueprints_create_request.py b/src/workos/agents/models/agent_blueprints_create_request.py index e31f0180..8abce2b0 100644 --- a/src/workos/agents/models/agent_blueprints_create_request.py +++ b/src/workos/agents/models/agent_blueprints_create_request.py @@ -7,6 +7,9 @@ from workos._types import _raise_deserialize_error +from .agent_blueprints_create_request_invocable_as import ( + AgentBlueprintsCreateRequestInvocableAs, +) from .agent_blueprints_create_request_invocable_by import ( AgentBlueprintsCreateRequestInvocableBy, ) @@ -27,6 +30,8 @@ class AgentBlueprintsCreateRequest: """Permission slugs forming the ceiling on what sessions minted from this blueprint may do. Each slug must exist in the environment.""" invocable_by: AgentBlueprintsCreateRequestInvocableBy | None = None """Who may mint sessions from this blueprint.""" + invocable_as: AgentBlueprintsCreateRequestInvocableAs | None = None + """How sessions may be minted from this blueprint.""" session_settings: AgentBlueprintsCreateRequestSessionSetting | None = None """Token and session lifetimes for sessions minted from this blueprint.""" @@ -43,6 +48,11 @@ def from_dict(cls, data: dict[str, Any]) -> AgentBlueprintsCreateRequest: ) if (_v_invocable_by := data.get("invocable_by")) is not None else None, + invocable_as=AgentBlueprintsCreateRequestInvocableAs.from_dict( + cast(dict[str, Any], _v_invocable_as) + ) + if (_v_invocable_as := data.get("invocable_as")) is not None + else None, session_settings=AgentBlueprintsCreateRequestSessionSetting.from_dict( cast(dict[str, Any], _v_session_settings) ) @@ -62,6 +72,8 @@ def to_dict(self) -> dict[str, Any]: result["permissions"] = self.permissions if self.invocable_by is not None: result["invocable_by"] = self.invocable_by.to_dict() + if self.invocable_as is not None: + result["invocable_as"] = self.invocable_as.to_dict() if self.session_settings is not None: result["session_settings"] = self.session_settings.to_dict() return result diff --git a/src/workos/agents/models/agent_blueprints_create_request_invocable_as.py b/src/workos/agents/models/agent_blueprints_create_request_invocable_as.py new file mode 100644 index 00000000..c2d37dd2 --- /dev/null +++ b/src/workos/agents/models/agent_blueprints_create_request_invocable_as.py @@ -0,0 +1,47 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from enum import Enum +from typing import Any, cast + +from workos._types import _raise_deserialize_error +from workos.common.models.agent_blueprints_create_request_invocable_as_instance_types import ( + AgentBlueprintsCreateRequestInvocableAsInstanceTypes, +) + + +@dataclass(slots=True) +class AgentBlueprintsCreateRequestInvocableAs: + """How sessions may be minted from this blueprint.""" + + instance_types: ( + list[AgentBlueprintsCreateRequestInvocableAsInstanceTypes] | None + ) = None + """Instance types that may be minted from this blueprint: `delegated` (a user-delegated session, minted with a user access token) and/or `autonomous` (a session acting as the agent itself, minted with an agent registration credential). Agent-delegated chaining and refreshes are checked against the type of the presenting instance. Defaults to both.""" + + @classmethod + def from_dict(cls, data: dict[str, Any]) -> AgentBlueprintsCreateRequestInvocableAs: + """Deserialize from a dictionary.""" + try: + return cls( + instance_types=[ + AgentBlueprintsCreateRequestInvocableAsInstanceTypes(item) + for item in cast(list[Any], _v_instance_types) + ] + if (_v_instance_types := data.get("instance_types")) is not None + else None, + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error("AgentBlueprintsCreateRequestInvocableAs", e) + + def to_dict(self) -> dict[str, Any]: + """Serialize to a dictionary.""" + result: dict[str, Any] = {} + if self.instance_types is not None: + result["instance_types"] = [ + item.value if isinstance(item, Enum) else item + for item in self.instance_types + ] + return result diff --git a/src/workos/common/models/agent_blueprint_created_data.py b/src/workos/common/models/agent_blueprint_created_data.py index 987d068b..6b83419c 100644 --- a/src/workos/common/models/agent_blueprint_created_data.py +++ b/src/workos/common/models/agent_blueprint_created_data.py @@ -7,6 +7,9 @@ from workos._types import _raise_deserialize_error +from .agent_blueprint_created_data_invocable_as import ( + AgentBlueprintCreatedDataInvocableAs, +) from .agent_blueprint_created_data_invocable_by import ( AgentBlueprintCreatedDataInvocableBy, ) @@ -31,6 +34,8 @@ class AgentBlueprintCreatedData: """Permission slugs forming the ceiling on what sessions minted from this blueprint may do.""" invocable_by: AgentBlueprintCreatedDataInvocableBy """Who may mint sessions from this blueprint.""" + invocable_as: AgentBlueprintCreatedDataInvocableAs + """How sessions may be minted from this blueprint.""" session_settings: AgentBlueprintCreatedDataSessionSetting """Token and session lifetimes for sessions minted from this blueprint.""" created_at: str @@ -51,6 +56,9 @@ def from_dict(cls, data: dict[str, Any]) -> AgentBlueprintCreatedData: invocable_by=AgentBlueprintCreatedDataInvocableBy.from_dict( cast(dict[str, Any], data["invocable_by"]) ), + invocable_as=AgentBlueprintCreatedDataInvocableAs.from_dict( + cast(dict[str, Any], data["invocable_as"]) + ), session_settings=AgentBlueprintCreatedDataSessionSetting.from_dict( cast(dict[str, Any], data["session_settings"]) ), @@ -72,6 +80,7 @@ def to_dict(self) -> dict[str, Any]: result["description"] = None result["permissions"] = self.permissions result["invocable_by"] = self.invocable_by.to_dict() + result["invocable_as"] = self.invocable_as.to_dict() result["session_settings"] = self.session_settings.to_dict() result["created_at"] = self.created_at result["updated_at"] = self.updated_at diff --git a/src/workos/common/models/agent_blueprint_created_data_invocable_as.py b/src/workos/common/models/agent_blueprint_created_data_invocable_as.py new file mode 100644 index 00000000..4d53f921 --- /dev/null +++ b/src/workos/common/models/agent_blueprint_created_data_invocable_as.py @@ -0,0 +1,43 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from enum import Enum +from typing import Any, cast + +from workos._types import _raise_deserialize_error + +from .agent_blueprint_created_data_invocable_as_instance_types import ( + AgentBlueprintCreatedDataInvocableAsInstanceTypes, +) + + +@dataclass(slots=True) +class AgentBlueprintCreatedDataInvocableAs: + """How sessions may be minted from this blueprint.""" + + instance_types: list[AgentBlueprintCreatedDataInvocableAsInstanceTypes] + """Instance types that may be minted from this blueprint: `delegated` (acting for a user) and/or `autonomous` (acting as the agent itself).""" + + @classmethod + def from_dict(cls, data: dict[str, Any]) -> AgentBlueprintCreatedDataInvocableAs: + """Deserialize from a dictionary.""" + try: + return cls( + instance_types=[ + AgentBlueprintCreatedDataInvocableAsInstanceTypes(item) + for item in cast(list[Any], data["instance_types"]) + ], + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error("AgentBlueprintCreatedDataInvocableAs", e) + + def to_dict(self) -> dict[str, Any]: + """Serialize to a dictionary.""" + result: dict[str, Any] = {} + result["instance_types"] = [ + item.value if isinstance(item, Enum) else item + for item in self.instance_types + ] + return result diff --git a/src/workos/common/models/agent_blueprint_created_data_invocable_as_instance_types.py b/src/workos/common/models/agent_blueprint_created_data_invocable_as_instance_types.py new file mode 100644 index 00000000..2fc69238 --- /dev/null +++ b/src/workos/common/models/agent_blueprint_created_data_invocable_as_instance_types.py @@ -0,0 +1,31 @@ +# This file is auto-generated by oagen. Do not edit. + +"""Enumeration of agent blueprint created data invocable as instance types values.""" + +from __future__ import annotations + +from enum import Enum +from typing import Literal, TypeAlias + + +class AgentBlueprintCreatedDataInvocableAsInstanceTypes(str, Enum): + """Known values for AgentBlueprintCreatedDataInvocableAsInstanceTypes.""" + + DELEGATED = "delegated" + AUTONOMOUS = "autonomous" + + @classmethod + def _missing_( + cls, value: object + ) -> AgentBlueprintCreatedDataInvocableAsInstanceTypes | None: + if not isinstance(value, str): + return None + unknown = str.__new__(cls, value) + unknown._name_ = value.upper() + unknown._value_ = value + return unknown + + +AgentBlueprintCreatedDataInvocableAsInstanceTypesLiteral: TypeAlias = Literal[ + "delegated", "autonomous" +] diff --git a/src/workos/common/models/agent_blueprints_create_request_invocable_as_instance_types.py b/src/workos/common/models/agent_blueprints_create_request_invocable_as_instance_types.py new file mode 100644 index 00000000..097f0bf2 --- /dev/null +++ b/src/workos/common/models/agent_blueprints_create_request_invocable_as_instance_types.py @@ -0,0 +1,12 @@ +# This file is auto-generated by oagen. Do not edit. + +from typing import TypeAlias + +from .agent_blueprint_created_data_invocable_as_instance_types import ( + AgentBlueprintCreatedDataInvocableAsInstanceTypes, +) + +AgentBlueprintsCreateRequestInvocableAsInstanceTypes: TypeAlias = ( + AgentBlueprintCreatedDataInvocableAsInstanceTypes +) +__all__ = ["AgentBlueprintsCreateRequestInvocableAsInstanceTypes"] diff --git a/src/workos/common/models/agent_instance_created_data_type.py b/src/workos/common/models/agent_instance_created_data_type.py index c2f549d2..4dca6fba 100644 --- a/src/workos/common/models/agent_instance_created_data_type.py +++ b/src/workos/common/models/agent_instance_created_data_type.py @@ -1,27 +1,12 @@ # This file is auto-generated by oagen. Do not edit. -"""Enumeration of agent instance created data type values.""" +from typing import TypeAlias -from __future__ import annotations +from .agent_blueprint_created_data_invocable_as_instance_types import ( + AgentBlueprintCreatedDataInvocableAsInstanceTypes, +) -from enum import Enum -from typing import Literal, TypeAlias - - -class AgentInstanceCreatedDataType(str, Enum): - """Known values for AgentInstanceCreatedDataType.""" - - DELEGATED = "delegated" - AUTONOMOUS = "autonomous" - - @classmethod - def _missing_(cls, value: object) -> AgentInstanceCreatedDataType | None: - if not isinstance(value, str): - return None - unknown = str.__new__(cls, value) - unknown._name_ = value.upper() - unknown._value_ = value - return unknown - - -AgentInstanceCreatedDataTypeLiteral: TypeAlias = Literal["delegated", "autonomous"] +AgentInstanceCreatedDataType: TypeAlias = ( + AgentBlueprintCreatedDataInvocableAsInstanceTypes +) +__all__ = ["AgentInstanceCreatedDataType"] diff --git a/src/workos/common/models/create_data_integration_ownership.py b/src/workos/common/models/create_data_integration_ownership.py index f2eef58b..41980fdb 100644 --- a/src/workos/common/models/create_data_integration_ownership.py +++ b/src/workos/common/models/create_data_integration_ownership.py @@ -1,27 +1,12 @@ # This file is auto-generated by oagen. Do not edit. -"""Enumeration of create data integration ownership values.""" +from typing import TypeAlias -from __future__ import annotations +from .add_data_integrations_get_data_integration_authorize_url_request_connection_owner import ( + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner, +) -from enum import Enum -from typing import Literal, TypeAlias - - -class CreateDataIntegrationOwnership(str, Enum): - """Known values for CreateDataIntegrationOwnership.""" - - USER = "user" - ORGANIZATION = "organization" - - @classmethod - def _missing_(cls, value: object) -> CreateDataIntegrationOwnership | None: - if not isinstance(value, str): - return None - unknown = str.__new__(cls, value) - unknown._name_ = value.upper() - unknown._value_ = value - return unknown - - -CreateDataIntegrationOwnershipLiteral: TypeAlias = Literal["user", "organization"] +CreateDataIntegrationOwnership: TypeAlias = ( + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner +) +__all__ = ["CreateDataIntegrationOwnership"] diff --git a/src/workos/common/models/data_integrations_create_api_key_connection_request_connection_owner.py b/src/workos/common/models/data_integrations_create_api_key_connection_request_connection_owner.py new file mode 100644 index 00000000..d2d25918 --- /dev/null +++ b/src/workos/common/models/data_integrations_create_api_key_connection_request_connection_owner.py @@ -0,0 +1,12 @@ +# This file is auto-generated by oagen. Do not edit. + +from typing import TypeAlias + +from .add_data_integrations_get_data_integration_authorize_url_request_connection_owner import ( + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner, +) + +DataIntegrationsCreateApiKeyConnectionRequestConnectionOwner: TypeAlias = ( + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner +) +__all__ = ["DataIntegrationsCreateApiKeyConnectionRequestConnectionOwner"] diff --git a/tests/fixtures/agent_blueprint_created.json b/tests/fixtures/agent_blueprint_created.json index a5dbe363..cea3f937 100644 --- a/tests/fixtures/agent_blueprint_created.json +++ b/tests/fixtures/agent_blueprint_created.json @@ -19,6 +19,12 @@ "org_01EHWNCE74X7JSDV0X3SZ3KJNY" ] }, + "invocable_as": { + "instance_types": [ + "delegated", + "autonomous" + ] + }, "session_settings": { "max_age_seconds": 3600, "access_token_ttl_seconds": 300, diff --git a/tests/fixtures/agent_blueprint_created_data.json b/tests/fixtures/agent_blueprint_created_data.json index 942ec8f2..0f4e69cd 100644 --- a/tests/fixtures/agent_blueprint_created_data.json +++ b/tests/fixtures/agent_blueprint_created_data.json @@ -15,6 +15,12 @@ "org_01EHWNCE74X7JSDV0X3SZ3KJNY" ] }, + "invocable_as": { + "instance_types": [ + "delegated", + "autonomous" + ] + }, "session_settings": { "max_age_seconds": 3600, "access_token_ttl_seconds": 300, diff --git a/tests/fixtures/agent_blueprint_created_data_invocable_as.json b/tests/fixtures/agent_blueprint_created_data_invocable_as.json new file mode 100644 index 00000000..bfd33188 --- /dev/null +++ b/tests/fixtures/agent_blueprint_created_data_invocable_as.json @@ -0,0 +1,6 @@ +{ + "instance_types": [ + "delegated", + "autonomous" + ] +} diff --git a/tests/fixtures/agent_blueprints_create_request.json b/tests/fixtures/agent_blueprints_create_request.json index f7a2ddd1..8d83cb7e 100644 --- a/tests/fixtures/agent_blueprints_create_request.json +++ b/tests/fixtures/agent_blueprints_create_request.json @@ -13,6 +13,12 @@ "org_01EHWNCE74X7JSDV0X3SZ3KJNY" ] }, + "invocable_as": { + "instance_types": [ + "delegated", + "autonomous" + ] + }, "session_settings": { "max_age_seconds": 3600, "access_token_ttl_seconds": 300, diff --git a/tests/fixtures/agent_blueprints_create_request_invocable_as.json b/tests/fixtures/agent_blueprints_create_request_invocable_as.json new file mode 100644 index 00000000..bfd33188 --- /dev/null +++ b/tests/fixtures/agent_blueprints_create_request_invocable_as.json @@ -0,0 +1,6 @@ +{ + "instance_types": [ + "delegated", + "autonomous" + ] +} diff --git a/tests/fixtures/data_integrations_create_api_key_connection_request.json b/tests/fixtures/data_integrations_create_api_key_connection_request.json new file mode 100644 index 00000000..e37baa56 --- /dev/null +++ b/tests/fixtures/data_integrations_create_api_key_connection_request.json @@ -0,0 +1,9 @@ +{ + "account_display_name": "Acme production", + "account_identifier": "workspace_123", + "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + "connection_owner": "user", + "secret": "sk-1234567890abcdef", + "connection_intent": "add" +} diff --git a/tests/fixtures/generate_link.json b/tests/fixtures/generate_link.json index 0630573b..4246a564 100644 --- a/tests/fixtures/generate_link.json +++ b/tests/fixtures/generate_link.json @@ -2,6 +2,7 @@ "return_url": "https://example.com/admin-portal/return", "success_url": "https://example.com/admin-portal/success", "organization": "org_01EHZNVPK3SFK441A1RGBFSHRT", + "external_user_id": "user_12345", "intent": "sso", "it_contact_emails": [ "it-contact@example.com" diff --git a/tests/fixtures/revoke_link.json b/tests/fixtures/revoke_link.json new file mode 100644 index 00000000..2e35fb48 --- /dev/null +++ b/tests/fixtures/revoke_link.json @@ -0,0 +1,4 @@ +{ + "organization": "org_01EHZNVPK3SFK441A1RGBFSHRT", + "external_user_id": "user_12345" +} diff --git a/tests/test_admin_portal.py b/tests/test_admin_portal.py index 1f82b1ec..f75c374c 100644 --- a/tests/test_admin_portal.py +++ b/tests/test_admin_portal.py @@ -26,7 +26,7 @@ def test_generate_link(self, workos, httpx_mock): assert isinstance(result, PortalLinkResponse) assert ( result.link - == "https://setup.workos.com?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + == "https://setup.workos.com/init?apiToken=01EHZNVPK3SFK441A1RGBFSHRT" ) request = httpx_mock.get_request() assert request.method == "POST" @@ -34,6 +34,15 @@ def test_generate_link(self, workos, httpx_mock): body = json.loads(request.content) assert body["organization"] == "test_organization" + def test_create_revoke_link(self, workos, httpx_mock): + httpx_mock.add_response(json={}) + workos.admin_portal.create_revoke_link( + organization="test_organization", external_user_id="test_external_user_id" + ) + request = httpx_mock.get_request() + assert request.method == "POST" + assert request.url.path.endswith("/portal/revoke_link") + def test_generate_link_with_request_options(self, workos, httpx_mock): httpx_mock.add_response(json=load_fixture("portal_link_response.json")) workos.admin_portal.generate_link( @@ -121,12 +130,22 @@ async def test_generate_link(self, async_workos, httpx_mock): assert isinstance(result, PortalLinkResponse) assert ( result.link - == "https://setup.workos.com?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + == "https://setup.workos.com/init?apiToken=01EHZNVPK3SFK441A1RGBFSHRT" ) request = httpx_mock.get_request() assert request.method == "POST" assert request.url.path.endswith("/portal/generate_link") + @pytest.mark.asyncio + async def test_create_revoke_link(self, async_workos, httpx_mock): + httpx_mock.add_response(json={}) + await async_workos.admin_portal.create_revoke_link( + organization="test_organization", external_user_id="test_external_user_id" + ) + request = httpx_mock.get_request() + assert request.method == "POST" + assert request.url.path.endswith("/portal/revoke_link") + @pytest.mark.asyncio async def test_generate_link_with_request_options(self, async_workos, httpx_mock): httpx_mock.add_response(json=load_fixture("portal_link_response.json")) diff --git a/tests/test_admin_portal_models_round_trip.py b/tests/test_admin_portal_models_round_trip.py index 3c168023..315f97f2 100644 --- a/tests/test_admin_portal_models_round_trip.py +++ b/tests/test_admin_portal_models_round_trip.py @@ -17,7 +17,7 @@ def test_portal_link_response_round_trip(self): def test_portal_link_response_minimal_payload(self): data = { - "link": "https://setup.workos.com?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + "link": "https://setup.workos.com/init?apiToken=01EHZNVPK3SFK441A1RGBFSHRT" } instance = PortalLinkResponse.from_dict(data) serialized = instance.to_dict() From b82caf936b2c1a77abecf6fdbe58f11eda85a049 Mon Sep 17 00:00:00 2001 From: "workos-sdk-automation[bot]" <255426317+workos-sdk-automation[bot]@users.noreply.github.com> Date: Wed, 14 Oct 2026 21:31:32 +0000 Subject: [PATCH 3/8] feat(audit_logs): Update audit log API surface --- src/workos/audit_logs/_resource.py | 4 ---- ...data_integrations_list_response_data_connection_owner.py | 6 ++++-- .../models/resource_export_completed_data_resource_type.py | 2 ++ tests/fixtures/data_integrations_list_response.json | 4 ++++ tests/fixtures/data_integrations_list_response_data.json | 4 ++++ tests/fixtures/list_agent_blueprint.json | 6 ++++++ 6 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/workos/audit_logs/_resource.py b/src/workos/audit_logs/_resource.py index 49c1437b..7c632bf9 100644 --- a/src/workos/audit_logs/_resource.py +++ b/src/workos/audit_logs/_resource.py @@ -277,8 +277,6 @@ def create_event( To achieve idempotency, you can add `Idempotency-Key` request header to a Create Event request with a unique string as the value. Each subsequent request matching this unique string will return the same response. We suggest using [v4 UUIDs](https://en.wikipedia.org/wiki/Universally_unique_identifier) for idempotency keys to avoid collisions. - Idempotency keys expire after 24 hours. The API will generate a new response if you submit a request with an expired key. - Args: organization_id: The unique ID of the Organization. event: The audit log event to create. @@ -634,8 +632,6 @@ async def create_event( To achieve idempotency, you can add `Idempotency-Key` request header to a Create Event request with a unique string as the value. Each subsequent request matching this unique string will return the same response. We suggest using [v4 UUIDs](https://en.wikipedia.org/wiki/Universally_unique_identifier) for idempotency keys to avoid collisions. - Idempotency keys expire after 24 hours. The API will generate a new response if you submit a request with an expired key. - Args: organization_id: The unique ID of the Organization. event: The audit log event to create. diff --git a/src/workos/common/models/data_integrations_list_response_data_connection_owner.py b/src/workos/common/models/data_integrations_list_response_data_connection_owner.py index 9286682a..5d614387 100644 --- a/src/workos/common/models/data_integrations_list_response_data_connection_owner.py +++ b/src/workos/common/models/data_integrations_list_response_data_connection_owner.py @@ -2,9 +2,11 @@ from typing import TypeAlias -from .create_data_integration_ownership import CreateDataIntegrationOwnership +from .add_data_integrations_get_data_integration_authorize_url_request_connection_owner import ( + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner, +) DataIntegrationsListResponseDataConnectionOwner: TypeAlias = ( - CreateDataIntegrationOwnership + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner ) __all__ = ["DataIntegrationsListResponseDataConnectionOwner"] diff --git a/src/workos/common/models/resource_export_completed_data_resource_type.py b/src/workos/common/models/resource_export_completed_data_resource_type.py index d9e58dc1..f1126ac0 100644 --- a/src/workos/common/models/resource_export_completed_data_resource_type.py +++ b/src/workos/common/models/resource_export_completed_data_resource_type.py @@ -18,6 +18,7 @@ class ResourceExportCompletedDataResourceType(str, Enum): AUDIT_LOG_EVENTS = "auditLogEvents" CONNECTIONS = "connections" DIRECTORY_USERS = "directoryUsers" + SCIM_LOGS = "scimLogs" @classmethod def _missing_(cls, value: object) -> ResourceExportCompletedDataResourceType | None: @@ -37,4 +38,5 @@ def _missing_(cls, value: object) -> ResourceExportCompletedDataResourceType | N "auditLogEvents", "connections", "directoryUsers", + "scimLogs", ] diff --git a/tests/fixtures/data_integrations_list_response.json b/tests/fixtures/data_integrations_list_response.json index 6c46a818..32e1d723 100644 --- a/tests/fixtures/data_integrations_list_response.json +++ b/tests/fixtures/data_integrations_list_response.json @@ -40,6 +40,8 @@ "instance_url": "https://example.my.salesforce.com" }, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": null, "created_at": "2024-01-16T14:20:00.000Z", "updated_at": "2024-01-16T14:20:00.000Z", "userlandUserId": "test_userlandUserId" @@ -65,6 +67,8 @@ "instance_url": "https://example.my.salesforce.com" }, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": null, "created_at": "2024-01-16T14:20:00.000Z", "updated_at": "2024-01-16T14:20:00.000Z", "userlandUserId": "test_userlandUserId" diff --git a/tests/fixtures/data_integrations_list_response_data.json b/tests/fixtures/data_integrations_list_response_data.json index 582a3a34..becf1aed 100644 --- a/tests/fixtures/data_integrations_list_response_data.json +++ b/tests/fixtures/data_integrations_list_response_data.json @@ -37,6 +37,8 @@ "instance_url": "https://example.my.salesforce.com" }, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": null, "created_at": "2024-01-16T14:20:00.000Z", "updated_at": "2024-01-16T14:20:00.000Z", "userlandUserId": "test_userlandUserId" @@ -62,6 +64,8 @@ "instance_url": "https://example.my.salesforce.com" }, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": null, "created_at": "2024-01-16T14:20:00.000Z", "updated_at": "2024-01-16T14:20:00.000Z", "userlandUserId": "test_userlandUserId" diff --git a/tests/fixtures/list_agent_blueprint.json b/tests/fixtures/list_agent_blueprint.json index c2e5f08b..ef288267 100644 --- a/tests/fixtures/list_agent_blueprint.json +++ b/tests/fixtures/list_agent_blueprint.json @@ -17,6 +17,12 @@ "org_01EHWNCE74X7JSDV0X3SZ3KJNY" ] }, + "invocable_as": { + "instance_types": [ + "delegated", + "autonomous" + ] + }, "session_settings": { "max_age_seconds": 3600, "access_token_ttl_seconds": 300, From 096423b93aa2df08f8866cd9de10bd5b5a3fba52 Mon Sep 17 00:00:00 2001 From: "workos-sdk-automation[bot]" <255426317+workos-sdk-automation[bot]@users.noreply.github.com> Date: Wed, 14 Oct 2026 21:31:32 +0000 Subject: [PATCH 4/8] feat(feature_flags): Update feature flag API surface --- src/workos/feature_flags/_resource.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/workos/feature_flags/_resource.py b/src/workos/feature_flags/_resource.py index e3ee805a..3e375530 100644 --- a/src/workos/feature_flags/_resource.py +++ b/src/workos/feature_flags/_resource.py @@ -119,6 +119,7 @@ def disable_feature_flag( Raises: NotFoundError: If the resource is not found (404). + ConflictError: If a conflict occurs (409). AuthenticationError: If the API key is invalid (401). RateLimitExceededError: If rate limited (429). ServerError: If the server returns a 5xx error. @@ -149,6 +150,7 @@ def enable_feature_flag( Raises: NotFoundError: If the resource is not found (404). + ConflictError: If a conflict occurs (409). AuthenticationError: If the API key is invalid (401). RateLimitExceededError: If rate limited (429). ServerError: If the server returns a 5xx error. @@ -423,6 +425,7 @@ async def disable_feature_flag( Raises: NotFoundError: If the resource is not found (404). + ConflictError: If a conflict occurs (409). AuthenticationError: If the API key is invalid (401). RateLimitExceededError: If rate limited (429). ServerError: If the server returns a 5xx error. @@ -453,6 +456,7 @@ async def enable_feature_flag( Raises: NotFoundError: If the resource is not found (404). + ConflictError: If a conflict occurs (409). AuthenticationError: If the API key is invalid (401). RateLimitExceededError: If rate limited (429). ServerError: If the server returns a 5xx error. From 3614a6c3cb21c1b2384001d03484efab0720f50c Mon Sep 17 00:00:00 2001 From: "workos-sdk-automation[bot]" <255426317+workos-sdk-automation[bot]@users.noreply.github.com> Date: Wed, 14 Oct 2026 21:31:32 +0000 Subject: [PATCH 5/8] feat(pipes): Update Pipes API surface --- src/workos/common/models/connected_account.py | 21 +- .../connected_account_write_response.py | 173 +++++ ...cted_account_write_response_auth_method.py | 8 + ..._account_write_response_connection_role.py | 8 + .../connected_account_write_response_state.py | 8 + ...onnected_account_write_response_warning.py | 35 + .../models/create_connected_account_state.py | 8 + ...te_organization_connected_account_state.py | 8 + ...als_connection_request_connection_owner.py | 12 + ...ns_list_response_data_connected_account.py | 21 +- .../organization_connected_account_state.py | 8 + .../common/models/pipe_connected_account.py | 14 + src/workos/pipes/_resource.py | 598 +++++++++++++----- src/workos/pipes/models/__init__.py | 42 +- ..._data_integration_authorize_url_request.py | 81 +++ .../pipes/models/connected_account_input.py | 78 +++ .../pipes/models/create_connected_account.py | 83 +++ .../create_organization_connected_account.py | 87 +++ .../data_integration_credentials_response.py | 8 +- ...gration_credentials_response_credential.py | 57 -- .../data_integration_vended_credential.py | 70 ++ ...tions_create_api_key_connection_request.py | 80 +++ ...e_client_credentials_connection_request.py | 91 +++ ..._data_integration_authorize_url_request.py | 7 + ...ata_integrations_upsert_api_key_request.py | 19 +- ...a_integrations_upsert_api_key_request_2.py | 76 +++ ...tions_upsert_client_credentials_request.py | 19 +- ...ons_upsert_client_credentials_request_2.py | 91 +++ .../models/organization_connected_account.py | 82 +++ src/workos/pipes/models/pipes_ownership.py | 12 +- ..._data_integration_authorize_url_request.py | 86 +++ ...ata_integrations_upsert_api_key_request.py | 86 +++ ...tions_upsert_client_credentials_request.py | 95 +++ tests/fixtures/connected_account.json | 2 + tests/fixtures/connected_account_input.json | 2 + .../connected_account_write_response.json | 31 + ...nected_account_write_response_warning.json | 4 + tests/fixtures/create_connected_account.json | 13 + ...create_organization_connected_account.json | 14 + ...client_credentials_connection_request.json | 13 + ..._list_response_data_connected_account.json | 2 + .../organization_connected_account.json | 13 + tests/fixtures/pipe_connected_account.json | 2 + .../pipes_account_connection_connected.json | 2 + ...pipes_account_connection_disconnected.json | 2 + ...unt_connection_reauthorization_needed.json | 2 + .../pipes_connected_account_connected.json | 2 + .../pipes_connected_account_disconnected.json | 2 + ...nected_account_reauthorization_needed.json | 2 + tests/test_pipes.py | 221 +++++-- tests/test_pipes_models_round_trip.py | 281 ++++++-- 51 files changed, 2458 insertions(+), 324 deletions(-) create mode 100644 src/workos/common/models/connected_account_write_response.py create mode 100644 src/workos/common/models/connected_account_write_response_auth_method.py create mode 100644 src/workos/common/models/connected_account_write_response_connection_role.py create mode 100644 src/workos/common/models/connected_account_write_response_state.py create mode 100644 src/workos/common/models/connected_account_write_response_warning.py create mode 100644 src/workos/common/models/create_connected_account_state.py create mode 100644 src/workos/common/models/create_organization_connected_account_state.py create mode 100644 src/workos/common/models/data_integrations_create_client_credentials_connection_request_connection_owner.py create mode 100644 src/workos/common/models/organization_connected_account_state.py create mode 100644 src/workos/pipes/models/add_data_integrations_get_data_integration_authorize_url_request.py create mode 100644 src/workos/pipes/models/connected_account_input.py create mode 100644 src/workos/pipes/models/create_connected_account.py create mode 100644 src/workos/pipes/models/create_organization_connected_account.py delete mode 100644 src/workos/pipes/models/data_integration_credentials_response_credential.py create mode 100644 src/workos/pipes/models/data_integration_vended_credential.py create mode 100644 src/workos/pipes/models/data_integrations_create_api_key_connection_request.py create mode 100644 src/workos/pipes/models/data_integrations_create_client_credentials_connection_request.py create mode 100644 src/workos/pipes/models/data_integrations_upsert_api_key_request_2.py create mode 100644 src/workos/pipes/models/data_integrations_upsert_client_credentials_request_2.py create mode 100644 src/workos/pipes/models/organization_connected_account.py create mode 100644 src/workos/pipes/models/reauthorize_data_integrations_get_data_integration_authorize_url_request.py create mode 100644 src/workos/pipes/models/reauthorize_data_integrations_upsert_api_key_request.py create mode 100644 src/workos/pipes/models/reauthorize_data_integrations_upsert_client_credentials_request.py create mode 100644 tests/fixtures/connected_account_write_response.json create mode 100644 tests/fixtures/connected_account_write_response_warning.json create mode 100644 tests/fixtures/create_connected_account.json create mode 100644 tests/fixtures/create_organization_connected_account.json create mode 100644 tests/fixtures/data_integrations_create_client_credentials_connection_request.json create mode 100644 tests/fixtures/organization_connected_account.json diff --git a/src/workos/common/models/connected_account.py b/src/workos/common/models/connected_account.py index 2eb3a183..f0c33aa0 100644 --- a/src/workos/common/models/connected_account.py +++ b/src/workos/common/models/connected_account.py @@ -36,7 +36,7 @@ class ConnectedAccount: state: ConnectedAccountState """The state of the connected account: - `connected`: The connection is active and tokens are valid. -- `needs_reauthorization`: The user needs to reauthorize the connection, typically because required scopes have changed. +- `needs_reauthorization`: The user needs to reauthorize the connection, typically because required scopes have changed. `state_reason` explains why when WorkOS invalidated the connection itself. - `disconnected`: The connection has been disconnected.""" created_at: str """The timestamp when the connection was created.""" @@ -51,7 +51,14 @@ class ConnectedAccount: client_secret_last_4: str | None = None """The last four characters of the client secret supplied for this connection, or `null` when it can't be read. Only present when `auth_method` is `client_credentials`.""" config: dict[str, str] | None = None - """The connection-level configuration values stored for this connection — the fields the provider declares at `installation` scope, excluding any it declares as secret. Only present when `auth_method` is `client_credentials`.""" + """The connection-level configuration values stored for this connection — the fields the provider declares at `installation` scope, excluding secret and undeclared fields. Returned for every authentication method; empty when no stored values can be disclosed.""" + established_by_user_id: str | None = None + """The [User](https://workos.com/docs/reference/authkit/user) whose authorization established this connection. `null` when the connection is developer-managed or its establisher is unknown, and when the establishing member lost access to the organization — in which case `state_reason` is `establishing_member_membership_ended`.""" + state_reason: Literal["establishing_member_membership_ended"] | None = None + """Why WorkOS put the connection in its current `state`, or `null` when there is no WorkOS-recorded reason: +- `establishing_member_membership_ended`: The member who established this organization-owned connection is no longer an active member of the organization, so its credentials were cleared. An organization admin must provide new credentials. + +Additional values may be added; treat an unrecognized value as an unknown reason.""" @classmethod def from_dict(cls, data: dict[str, Any]) -> ConnectedAccount: @@ -76,6 +83,8 @@ def from_dict(cls, data: dict[str, Any]) -> ConnectedAccount: client_id=data.get("client_id"), client_secret_last_4=data.get("client_secret_last_4"), config=data.get("config"), + established_by_user_id=data.get("established_by_user_id"), + state_reason=data.get("state_reason"), ) except (KeyError, ValueError) as e: _raise_deserialize_error("ConnectedAccount", e) @@ -132,4 +141,12 @@ def to_dict(self) -> dict[str, Any]: result["client_secret_last_4"] = None if self.config is not None: result["config"] = self.config + if self.established_by_user_id is not None: + result["established_by_user_id"] = self.established_by_user_id + else: + result["established_by_user_id"] = None + if self.state_reason is not None: + result["state_reason"] = self.state_reason + else: + result["state_reason"] = None return result diff --git a/src/workos/common/models/connected_account_write_response.py b/src/workos/common/models/connected_account_write_response.py new file mode 100644 index 00000000..f95e6406 --- /dev/null +++ b/src/workos/common/models/connected_account_write_response.py @@ -0,0 +1,173 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from enum import Enum +from typing import Any, Literal, cast + +from workos._types import _raise_deserialize_error + +from .connected_account_write_response_auth_method import ( + ConnectedAccountWriteResponseAuthMethod, +) +from .connected_account_write_response_connection_role import ( + ConnectedAccountWriteResponseConnectionRole, +) +from .connected_account_write_response_state import ConnectedAccountWriteResponseState +from .connected_account_write_response_warning import ( + ConnectedAccountWriteResponseWarning, +) + + +@dataclass(slots=True) +class ConnectedAccountWriteResponse: + """Connected Account Write Response model.""" + + object: Literal["connected_account"] + """Distinguishes the connected account object.""" + id: str + """The unique identifier of the connected account.""" + connection_role: ConnectedAccountWriteResponseConnectionRole + """Whether this row is the compatibility connection visible to undeclared clients or a standard peer for plural-aware clients. The role does not indicate preference or creation order.""" + account_identifier: str | None + """A best-effort identifier for the provider account this connection points at. It is correlation metadata, not the connection identifier or a selector.""" + account_display_name: str | None + """A mutable, non-unique display name for the provider account connection.""" + user_id: str | None + """The [User](https://workos.com/docs/reference/authkit/user) identifier associated with this connection.""" + organization_id: str | None + """The [Organization](https://workos.com/docs/reference/organization) identifier associated with this connection, or `null` if not scoped to an organization.""" + scopes: list[str] + """The OAuth scopes granted for this connection.""" + state: ConnectedAccountWriteResponseState + """The state of the connected account: +- `connected`: The connection is active and tokens are valid. +- `needs_reauthorization`: The user needs to reauthorize the connection, typically because required scopes have changed. `state_reason` explains why when WorkOS invalidated the connection itself. +- `disconnected`: The connection has been disconnected.""" + created_at: str + """The timestamp when the connection was created.""" + updated_at: str + """The timestamp when the connection was last updated.""" + auth_method: ConnectedAccountWriteResponseAuthMethod | None = None + """The authentication method used for this connection (`oauth`, `api_key`, or `client_credentials`). Defaults to `oauth` if absent.""" + api_key_last_4: str | None = None + """The last four characters of the API key, or `null` for OAuth connections.""" + client_id: str | None = None + """The client ID supplied for this connection. Only present when `auth_method` is `client_credentials`.""" + client_secret_last_4: str | None = None + """The last four characters of the client secret supplied for this connection, or `null` when it can't be read. Only present when `auth_method` is `client_credentials`.""" + config: dict[str, str] | None = None + """The connection-level configuration values stored for this connection — the fields the provider declares at `installation` scope, excluding secret and undeclared fields. Returned for every authentication method; empty when no stored values can be disclosed.""" + established_by_user_id: str | None = None + """The [User](https://workos.com/docs/reference/authkit/user) whose authorization established this connection. `null` when the connection is developer-managed or its establisher is unknown, and when the establishing member lost access to the organization — in which case `state_reason` is `establishing_member_membership_ended`.""" + state_reason: Literal["establishing_member_membership_ended"] | None = None + """Why WorkOS put the connection in its current `state`, or `null` when there is no WorkOS-recorded reason: +- `establishing_member_membership_ended`: The member who established this organization-owned connection is no longer an active member of the organization, so its credentials were cleared. An organization admin must provide new credentials. + +Additional values may be added; treat an unrecognized value as an unknown reason.""" + warnings: list[ConnectedAccountWriteResponseWarning] | None = None + """Non-blocking warnings about an explicitly supplied label, omitted when no duplicate exists. Reads do not include this field.""" + + @classmethod + def from_dict(cls, data: dict[str, Any]) -> ConnectedAccountWriteResponse: + """Deserialize from a dictionary.""" + try: + return cls( + object=data.get("object", "connected_account"), + id=data["id"], + connection_role=ConnectedAccountWriteResponseConnectionRole( + data["connection_role"] + ), + account_identifier=data["account_identifier"], + account_display_name=data["account_display_name"], + user_id=data["user_id"], + organization_id=data["organization_id"], + scopes=data["scopes"], + state=ConnectedAccountWriteResponseState(data["state"]), + created_at=data["created_at"], + updated_at=data["updated_at"], + auth_method=ConnectedAccountWriteResponseAuthMethod(_v_auth_method) + if (_v_auth_method := data.get("auth_method")) is not None + else None, + api_key_last_4=data.get("api_key_last_4"), + client_id=data.get("client_id"), + client_secret_last_4=data.get("client_secret_last_4"), + config=data.get("config"), + established_by_user_id=data.get("established_by_user_id"), + state_reason=data.get("state_reason"), + warnings=[ + ConnectedAccountWriteResponseWarning.from_dict( + cast(dict[str, Any], item) + ) + for item in cast(list[Any], _v_warnings) + ] + if (_v_warnings := data.get("warnings")) is not None + else None, + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error("ConnectedAccountWriteResponse", e) + + def to_dict(self) -> dict[str, Any]: + """Serialize to a dictionary.""" + result: dict[str, Any] = {} + result["object"] = self.object + result["id"] = self.id + result["connection_role"] = ( + self.connection_role.value + if isinstance(self.connection_role, Enum) + else self.connection_role + ) + if self.account_identifier is not None: + result["account_identifier"] = self.account_identifier + else: + result["account_identifier"] = None + if self.account_display_name is not None: + result["account_display_name"] = self.account_display_name + else: + result["account_display_name"] = None + if self.user_id is not None: + result["user_id"] = self.user_id + else: + result["user_id"] = None + if self.organization_id is not None: + result["organization_id"] = self.organization_id + else: + result["organization_id"] = None + result["scopes"] = self.scopes + result["state"] = ( + self.state.value if isinstance(self.state, Enum) else self.state + ) + result["created_at"] = self.created_at + result["updated_at"] = self.updated_at + if self.auth_method is not None: + result["auth_method"] = ( + self.auth_method.value + if isinstance(self.auth_method, Enum) + else self.auth_method + ) + if self.api_key_last_4 is not None: + result["api_key_last_4"] = self.api_key_last_4 + else: + result["api_key_last_4"] = None + if self.client_id is not None: + result["client_id"] = self.client_id + else: + result["client_id"] = None + if self.client_secret_last_4 is not None: + result["client_secret_last_4"] = self.client_secret_last_4 + else: + result["client_secret_last_4"] = None + if self.config is not None: + result["config"] = self.config + if self.established_by_user_id is not None: + result["established_by_user_id"] = self.established_by_user_id + else: + result["established_by_user_id"] = None + if self.state_reason is not None: + result["state_reason"] = self.state_reason + else: + result["state_reason"] = None + if self.warnings is not None: + result["warnings"] = [item.to_dict() for item in self.warnings] + return result diff --git a/src/workos/common/models/connected_account_write_response_auth_method.py b/src/workos/common/models/connected_account_write_response_auth_method.py new file mode 100644 index 00000000..5638d8e8 --- /dev/null +++ b/src/workos/common/models/connected_account_write_response_auth_method.py @@ -0,0 +1,8 @@ +# This file is auto-generated by oagen. Do not edit. + +from typing import TypeAlias + +from .connected_account_auth_method import ConnectedAccountAuthMethod + +ConnectedAccountWriteResponseAuthMethod: TypeAlias = ConnectedAccountAuthMethod +__all__ = ["ConnectedAccountWriteResponseAuthMethod"] diff --git a/src/workos/common/models/connected_account_write_response_connection_role.py b/src/workos/common/models/connected_account_write_response_connection_role.py new file mode 100644 index 00000000..9a41502d --- /dev/null +++ b/src/workos/common/models/connected_account_write_response_connection_role.py @@ -0,0 +1,8 @@ +# This file is auto-generated by oagen. Do not edit. + +from typing import TypeAlias + +from .connected_account_connection_role import ConnectedAccountConnectionRole + +ConnectedAccountWriteResponseConnectionRole: TypeAlias = ConnectedAccountConnectionRole +__all__ = ["ConnectedAccountWriteResponseConnectionRole"] diff --git a/src/workos/common/models/connected_account_write_response_state.py b/src/workos/common/models/connected_account_write_response_state.py new file mode 100644 index 00000000..710990a5 --- /dev/null +++ b/src/workos/common/models/connected_account_write_response_state.py @@ -0,0 +1,8 @@ +# This file is auto-generated by oagen. Do not edit. + +from typing import TypeAlias + +from .connected_account_state import ConnectedAccountState + +ConnectedAccountWriteResponseState: TypeAlias = ConnectedAccountState +__all__ = ["ConnectedAccountWriteResponseState"] diff --git a/src/workos/common/models/connected_account_write_response_warning.py b/src/workos/common/models/connected_account_write_response_warning.py new file mode 100644 index 00000000..e7512bbd --- /dev/null +++ b/src/workos/common/models/connected_account_write_response_warning.py @@ -0,0 +1,35 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, Literal + +from workos._types import _raise_deserialize_error + + +@dataclass(slots=True) +class ConnectedAccountWriteResponseWarning: + """Connected Account Write Response Warning model.""" + + code: Literal["duplicate_account_display_name"] + message: str + """A human-readable explanation of the non-blocking warning.""" + + @classmethod + def from_dict(cls, data: dict[str, Any]) -> ConnectedAccountWriteResponseWarning: + """Deserialize from a dictionary.""" + try: + return cls( + code=data.get("code", "duplicate_account_display_name"), + message=data["message"], + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error("ConnectedAccountWriteResponseWarning", e) + + def to_dict(self) -> dict[str, Any]: + """Serialize to a dictionary.""" + result: dict[str, Any] = {} + result["code"] = self.code + result["message"] = self.message + return result diff --git a/src/workos/common/models/create_connected_account_state.py b/src/workos/common/models/create_connected_account_state.py new file mode 100644 index 00000000..bd11386c --- /dev/null +++ b/src/workos/common/models/create_connected_account_state.py @@ -0,0 +1,8 @@ +# This file is auto-generated by oagen. Do not edit. + +from typing import TypeAlias + +from .connected_account_input_state import ConnectedAccountInputState + +CreateConnectedAccountState: TypeAlias = ConnectedAccountInputState +__all__ = ["CreateConnectedAccountState"] diff --git a/src/workos/common/models/create_organization_connected_account_state.py b/src/workos/common/models/create_organization_connected_account_state.py new file mode 100644 index 00000000..1d6a8dbc --- /dev/null +++ b/src/workos/common/models/create_organization_connected_account_state.py @@ -0,0 +1,8 @@ +# This file is auto-generated by oagen. Do not edit. + +from typing import TypeAlias + +from .connected_account_input_state import ConnectedAccountInputState + +CreateOrganizationConnectedAccountState: TypeAlias = ConnectedAccountInputState +__all__ = ["CreateOrganizationConnectedAccountState"] diff --git a/src/workos/common/models/data_integrations_create_client_credentials_connection_request_connection_owner.py b/src/workos/common/models/data_integrations_create_client_credentials_connection_request_connection_owner.py new file mode 100644 index 00000000..0391e53b --- /dev/null +++ b/src/workos/common/models/data_integrations_create_client_credentials_connection_request_connection_owner.py @@ -0,0 +1,12 @@ +# This file is auto-generated by oagen. Do not edit. + +from typing import TypeAlias + +from .add_data_integrations_get_data_integration_authorize_url_request_connection_owner import ( + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner, +) + +DataIntegrationsCreateClientCredentialsConnectionRequestConnectionOwner: TypeAlias = ( + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner +) +__all__ = ["DataIntegrationsCreateClientCredentialsConnectionRequestConnectionOwner"] diff --git a/src/workos/common/models/data_integrations_list_response_data_connected_account.py b/src/workos/common/models/data_integrations_list_response_data_connected_account.py index fa3e57c8..6e003b37 100644 --- a/src/workos/common/models/data_integrations_list_response_data_connected_account.py +++ b/src/workos/common/models/data_integrations_list_response_data_connected_account.py @@ -42,7 +42,7 @@ class DataIntegrationsListResponseDataConnectedAccount: state: DataIntegrationsListResponseDataConnectedAccountState """The state of the connected account: - `connected`: The connection is active and tokens are valid. -- `needs_reauthorization`: The user needs to reauthorize the connection, typically because required scopes have changed. +- `needs_reauthorization`: The user needs to reauthorize the connection, typically because required scopes have changed. `state_reason` explains why when WorkOS invalidated the connection itself. - `disconnected`: The connection has been disconnected.""" created_at: str """The timestamp when the connection was created.""" @@ -59,7 +59,14 @@ class DataIntegrationsListResponseDataConnectedAccount: client_secret_last_4: str | None = None """The last four characters of the client secret supplied for this connection, or `null` when it can't be read. Only present when `auth_method` is `client_credentials`.""" config: dict[str, str] | None = None - """The connection-level configuration values stored for this connection — the fields the provider declares at `installation` scope, excluding any it declares as secret. Only present when `auth_method` is `client_credentials`.""" + """The connection-level configuration values stored for this connection — the fields the provider declares at `installation` scope, excluding secret and undeclared fields. Returned for every authentication method; empty when no stored values can be disclosed.""" + established_by_user_id: str | None = None + """The [User](https://workos.com/docs/reference/authkit/user) whose authorization established this connection. `null` when the connection is developer-managed or its establisher is unknown, and when the establishing member lost access to the organization — in which case `state_reason` is `establishing_member_membership_ended`.""" + state_reason: Literal["establishing_member_membership_ended"] | None = None + """Why WorkOS put the connection in its current `state`, or `null` when there is no WorkOS-recorded reason: +- `establishing_member_membership_ended`: The member who established this organization-owned connection is no longer an active member of the organization, so its credentials were cleared. An organization admin must provide new credentials. + +Additional values may be added; treat an unrecognized value as an unknown reason.""" userland_user_id: str | None = None """Use `user_id` instead. @@ -96,6 +103,8 @@ def from_dict( client_id=data.get("client_id"), client_secret_last_4=data.get("client_secret_last_4"), config=data.get("config"), + established_by_user_id=data.get("established_by_user_id"), + state_reason=data.get("state_reason"), userland_user_id=data.get("userlandUserId"), ) except (KeyError, ValueError) as e: @@ -155,6 +164,14 @@ def to_dict(self) -> dict[str, Any]: result["client_secret_last_4"] = None if self.config is not None: result["config"] = self.config + if self.established_by_user_id is not None: + result["established_by_user_id"] = self.established_by_user_id + else: + result["established_by_user_id"] = None + if self.state_reason is not None: + result["state_reason"] = self.state_reason + else: + result["state_reason"] = None if self.userland_user_id is not None: result["userlandUserId"] = self.userland_user_id else: diff --git a/src/workos/common/models/organization_connected_account_state.py b/src/workos/common/models/organization_connected_account_state.py new file mode 100644 index 00000000..effbd879 --- /dev/null +++ b/src/workos/common/models/organization_connected_account_state.py @@ -0,0 +1,8 @@ +# This file is auto-generated by oagen. Do not edit. + +from typing import TypeAlias + +from .connected_account_input_state import ConnectedAccountInputState + +OrganizationConnectedAccountState: TypeAlias = ConnectedAccountInputState +__all__ = ["OrganizationConnectedAccountState"] diff --git a/src/workos/common/models/pipe_connected_account.py b/src/workos/common/models/pipe_connected_account.py index 348465b7..01c61134 100644 --- a/src/workos/common/models/pipe_connected_account.py +++ b/src/workos/common/models/pipe_connected_account.py @@ -48,6 +48,10 @@ class PipeConnectedAccount: """How the connection authenticates. Historical events may omit this field.""" api_key_last_4: str | None = None """The last four characters of the API key, or null for other authentication methods. Historical events may omit this field.""" + established_by_user_id: str | None = None + """The ID of the User whose authorization established the connected account, or null when it is developer-managed, its establisher is unknown, or the establishing member left the organization. Historical events may omit this field.""" + state_reason: Literal["establishing_member_membership_ended"] | None = None + """Why WorkOS put the connected account in its current state, or null when there is no WorkOS-recorded reason. `establishing_member_membership_ended` means the member who established an organization-owned connection is no longer an active member. Additional values may be added. Historical events may omit this field.""" @classmethod def from_dict(cls, data: dict[str, Any]) -> PipeConnectedAccount: @@ -73,6 +77,8 @@ def from_dict(cls, data: dict[str, Any]) -> PipeConnectedAccount: if (_v_auth_method := data.get("auth_method")) is not None else None, api_key_last_4=data.get("api_key_last_4"), + established_by_user_id=data.get("established_by_user_id"), + state_reason=data.get("state_reason"), ) except (KeyError, ValueError) as e: _raise_deserialize_error("PipeConnectedAccount", e) @@ -122,4 +128,12 @@ def to_dict(self) -> dict[str, Any]: result["api_key_last_4"] = self.api_key_last_4 else: result["api_key_last_4"] = None + if self.established_by_user_id is not None: + result["established_by_user_id"] = self.established_by_user_id + else: + result["established_by_user_id"] = None + if self.state_reason is not None: + result["state_reason"] = self.state_reason + else: + result["state_reason"] = None return result diff --git a/src/workos/pipes/_resource.py b/src/workos/pipes/_resource.py index 91a88875..805ffbb3 100644 --- a/src/workos/pipes/_resource.py +++ b/src/workos/pipes/_resource.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, Literal if TYPE_CHECKING: from .._client import AsyncWorkOSClient, WorkOSClient @@ -11,14 +11,26 @@ from workos.common.models.connected_account_input_state import ( ConnectedAccountInputState, ) +from workos.common.models.connected_account_write_response import ( + ConnectedAccountWriteResponse, +) +from workos.common.models.create_connected_account_state import ( + CreateConnectedAccountState, +) from workos.common.models.create_data_integration_auth_methods import ( CreateDataIntegrationAuthMethods, ) from workos.common.models.create_data_integration_ownership import ( CreateDataIntegrationOwnership, ) -from workos.common.models.data_integrations_get_data_integration_authorize_url_request_connection_owner import ( - DataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner, +from workos.common.models.create_organization_connected_account_state import ( + CreateOrganizationConnectedAccountState, +) +from workos.common.models.data_integrations_create_api_key_connection_request_connection_owner import ( + DataIntegrationsCreateApiKeyConnectionRequestConnectionOwner, +) +from workos.common.models.data_integrations_create_client_credentials_connection_request_connection_owner import ( + DataIntegrationsCreateClientCredentialsConnectionRequestConnectionOwner, ) from workos.common.models.data_integrations_get_user_token_request_connection_owner import ( DataIntegrationsGetUserTokenRequestConnectionOwner, @@ -26,20 +38,18 @@ from workos.common.models.data_integrations_list_response import ( DataIntegrationsListResponse, ) -from workos.common.models.data_integrations_upsert_api_key_request_connection_owner import ( - DataIntegrationsUpsertApiKeyRequestConnectionOwner, -) -from workos.common.models.data_integrations_upsert_client_credentials_request_connection_owner import ( - DataIntegrationsUpsertClientCredentialsRequestConnectionOwner, -) from workos.common.models.data_integrations_vend_credentials_request_connection_owner import ( DataIntegrationsVendCredentialsRequestConnectionOwner, ) +from workos.common.models.organization_connected_account_state import ( + OrganizationConnectedAccountState, +) from workos.common.models.pagination_order import PaginationOrder from .._pagination import AsyncPage, SyncPage from .._types import NOT_GIVEN, NotGiven, RequestOptions, enum_value from .models import ( + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequest, ApiKeyInstallation, CustomProviderDefinition, DataIntegration, @@ -47,7 +57,15 @@ DataIntegrationAuthorizeUrlResponse, DataIntegrationCredentialsInput, DataIntegrationCredentialsResponse, + DataIntegrationsGetDataIntegrationAuthorizeUrlRequest, + DataIntegrationsUpsertApiKeyRequest, + DataIntegrationsUpsertApiKeyRequest2, + DataIntegrationsUpsertClientCredentialsRequest, + DataIntegrationsUpsertClientCredentialsRequest2, PipesOwnership, + ReauthorizeDataIntegrationsGetDataIntegrationAuthorizeUrlRequest, + ReauthorizeDataIntegrationsUpsertApiKeyRequest, + ReauthorizeDataIntegrationsUpsertClientCredentialsRequest, UpdateCustomProviderDefinition, ) @@ -305,34 +323,40 @@ def delete_data_integration( request_options=request_options, ) - def update_data_integration_api_key( + def create_data_integration_api_key( self, slug: str, *, user_id: str, secret: str, + connection_intent: Literal["add"], + account_display_name: str | None | NotGiven = NOT_GIVEN, + account_identifier: str | None | NotGiven = NOT_GIVEN, organization_id: str | None = None, - connected_account_id: str | None = None, - connection_owner: DataIntegrationsUpsertApiKeyRequestConnectionOwner + connection_owner: DataIntegrationsCreateApiKeyConnectionRequestConnectionOwner | str | None = None, + idempotency_key: str | None = None, request_options: RequestOptions | None = None, - ) -> ConnectedAccount: - """Upsert an API key for a connected account + ) -> ConnectedAccountWriteResponse: + """Create another API key connected account - Creates or updates an API-key-based installation for the specified integration, owned by the user or, when `connection_owner` is `organization`, shared by the organization. If an installation already exists, the stored API key is rotated to the new value. + Creates another API key-based connected account for the specified integration, owned by the user or, when `connection_owner` is `organization`, shared by the organization. Requires `connection_intent: add` and does not accept `connected_account_id`; use PUT to create or rotate the compatibility connection or to update an exact connection. Creating an additional connection is not yet available: until it is, this endpoint succeeds only when the owner has no connection for this integration, which creates the compatibility connection, and otherwise returns 404 `multiple_connections_unavailable`. Args: slug: The identifier of the integration. + account_display_name: A mutable, non-unique label. Omit to preserve an existing label; null clears it. Labels are never account identifiers. + account_identifier: A developer-attested provider account identifier, never inferred from a label or credential. Omit to preserve an existing identifier; null clears it. user_id: A [User](https://workos.com/docs/reference/authkit/user) identifier. organization_id: An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to scope the connection to a specific organization. Required when `connection_owner` is `organization`. - connected_account_id: A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to rotate a specific existing connection. connection_owner: Whose connection to create or rotate. `user` (the default) addresses the connection owned by `user_id`. `organization` addresses the connection shared by every member of `organization_id`; `user_id` then identifies the member performing the request and must be an active member of the organization. secret: The API key secret to store for this integration. + connection_intent: Must be `add`: this endpoint only creates another connection. The first connection for an owner shape fills the compatibility slot; later connections are standard. Creating an additional connection is not yet available: until it is, `add` succeeds only when the owner has no connection for this integration and otherwise returns 404 `multiple_connections_unavailable`. + idempotency_key: Optional idempotency key for safe retries. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: - ConnectedAccount + ConnectedAccountWriteResponse Raises: BadRequestError: If the request is malformed (400). @@ -340,7 +364,6 @@ def update_data_integration_api_key( AuthorizationError: If the request is forbidden (403). NotFoundError: If the resource is not found (404). ConflictError: If a conflict occurs (409). - UnprocessableEntityError: If the request data is unprocessable (422). RateLimitExceededError: If rate limited (429). ServerError: If the server returns a 5xx error. """ @@ -349,19 +372,65 @@ def update_data_integration_api_key( for k, v in { "user_id": user_id, "organization_id": organization_id, - "connected_account_id": connected_account_id, "connection_owner": enum_value(connection_owner) if connection_owner is not None else None, "secret": secret, + "connection_intent": connection_intent, }.items() if v is not None } + if not isinstance(account_display_name, NotGiven): + body["account_display_name"] = account_display_name + if not isinstance(account_identifier, NotGiven): + body["account_identifier"] = account_identifier return self._client.request( - method="put", + method="post", path=("data-integrations", str(slug), "api-key"), body=body, - model=ConnectedAccount, + model=ConnectedAccountWriteResponse, + idempotency_key=idempotency_key, + request_options=request_options, + ) + + def update_data_integration_api_key( + self, + slug: str, + *, + body: DataIntegrationsUpsertApiKeyRequest + | ReauthorizeDataIntegrationsUpsertApiKeyRequest + | DataIntegrationsUpsertApiKeyRequest2 + | dict[str, Any], + request_options: RequestOptions | None = None, + ) -> ConnectedAccountWriteResponse: + """Upsert an API key for a connected account + + Creates or updates an API-key-based installation for the specified integration, owned by the user or, when `connection_owner` is `organization`, shared by the organization. If an installation already exists, the stored API key is rotated to the new value. To create another connection, use POST. + + Args: + slug: The identifier of the integration. + body: The request body. Accepts: DataIntegrationsUpsertApiKeyRequest, ReauthorizeDataIntegrationsUpsertApiKeyRequest, DataIntegrationsUpsertApiKeyRequest2, or a plain dict. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + ConnectedAccountWriteResponse + + Raises: + BadRequestError: If the request is malformed (400). + AuthenticationError: If the API key is invalid (401). + AuthorizationError: If the request is forbidden (403). + NotFoundError: If the resource is not found (404). + ConflictError: If a conflict occurs (409). + UnprocessableEntityError: If the request data is unprocessable (422). + RateLimitExceededError: If rate limited (429). + ServerError: If the server returns a 5xx error. + """ + _body: dict[str, Any] = body if isinstance(body, dict) else body.to_dict() + return self._client.request( + method="put", + path=("data-integrations", str(slug), "api-key"), + body=_body, + model=ConnectedAccountWriteResponse, request_options=request_options, ) @@ -369,13 +438,10 @@ def authorize_data_integration( self, slug: str, *, - user_id: str, - organization_id: str | None = None, - connection_owner: DataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner - | str - | None = None, - return_to: str | None = None, - config: dict[str, str] | None = None, + body: DataIntegrationsGetDataIntegrationAuthorizeUrlRequest + | AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequest + | ReauthorizeDataIntegrationsGetDataIntegrationAuthorizeUrlRequest + | dict[str, Any], request_options: RequestOptions | None = None, ) -> DataIntegrationAuthorizeUrlResponse: """Get authorization URL @@ -384,11 +450,7 @@ def authorize_data_integration( Args: slug: The slug identifier of the provider (e.g., `github`, `slack`, `notion`). - user_id: The ID of the user to authorize. When `connection_owner` is `organization`, this is the user authorizing on behalf of the organization; they must be an active member of the organization and do not become the owner of the resulting connected account. - organization_id: An organization ID to scope the authorization to a specific organization. Required when `connection_owner` is `organization`. - connection_owner: Who will own the connected account. `user` (the default) connects the user's own account. `organization` connects the organization's shared account and requires `organization_id`. - return_to: The URL to redirect the user to after authorization. - config: Connect-time config values for the provider-declared `installation`-scope fields (e.g. a Zendesk `subdomain`), keyed by the config field. Only fields the provider declares may be supplied, and required fields must be provided unless already pinned on the integration. + body: The request body. Accepts: DataIntegrationsGetDataIntegrationAuthorizeUrlRequest, AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequest, ReauthorizeDataIntegrationsGetDataIntegrationAuthorizeUrlRequest, or a plain dict. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -399,62 +461,58 @@ def authorize_data_integration( AuthenticationError: If the API key is invalid (401). AuthorizationError: If the request is forbidden (403). NotFoundError: If the resource is not found (404). + ConflictError: If a conflict occurs (409). + UnprocessableEntityError: If the request data is unprocessable (422). RateLimitExceededError: If rate limited (429). ServerError: If the server returns a 5xx error. """ - body: dict[str, Any] = { - k: v - for k, v in { - "user_id": user_id, - "organization_id": organization_id, - "connection_owner": enum_value(connection_owner) - if connection_owner is not None - else None, - "return_to": return_to, - "config": config, - }.items() - if v is not None - } + _body: dict[str, Any] = body if isinstance(body, dict) else body.to_dict() return self._client.request( method="post", path=("data-integrations", str(slug), "authorize"), - body=body, + body=_body, model=DataIntegrationAuthorizeUrlResponse, request_options=request_options, ) - def update_data_integration_client_credentials( + def create_data_integration_client_credential( self, slug: str, *, user_id: str, client_id: str, client_secret: str, + connection_intent: Literal["add"], + account_display_name: str | None | NotGiven = NOT_GIVEN, + account_identifier: str | None | NotGiven = NOT_GIVEN, organization_id: str | None = None, - connected_account_id: str | None = None, - connection_owner: DataIntegrationsUpsertClientCredentialsRequestConnectionOwner + connection_owner: DataIntegrationsCreateClientCredentialsConnectionRequestConnectionOwner | str | None = None, config: dict[str, str] | None = None, + idempotency_key: str | None = None, request_options: RequestOptions | None = None, - ) -> ConnectedAccount: - """Upsert client credentials for a connected account + ) -> ConnectedAccountWriteResponse: + """Create another client credentials connected account - Creates or updates a client-credentials-based installation for the specified integration, owned by the user or, when `connection_owner` is `organization`, shared by the organization. If an installation already exists, the stored client credentials are rotated to the new values. + Creates another client credentials-based connected account for the specified integration, owned by the user or, when `connection_owner` is `organization`, shared by the organization. Requires `connection_intent: add` and does not accept `connected_account_id`; use PUT to create or rotate the compatibility connection or to update an exact connection. Creating an additional connection is not yet available: until it is, this endpoint succeeds only when the owner has no connection for this integration, which creates the compatibility connection, and otherwise returns 404 `multiple_connections_unavailable`. Args: slug: The identifier of the integration. + account_display_name: A mutable, non-unique label. Omit to preserve an existing label; null clears it. Labels are never account identifiers. + account_identifier: A developer-attested provider account identifier, never inferred from a label or credential. Omit to preserve an existing identifier; null clears it. user_id: A [User](https://workos.com/docs/reference/authkit/user) identifier. organization_id: An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to scope the connection to a specific organization. Required when `connection_owner` is `organization`. - connected_account_id: A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to rotate a specific existing connection. connection_owner: Whose connection to create or rotate. `user` (the default) addresses the connection owned by `user_id`. `organization` addresses the connection shared by every member of `organization_id`; `user_id` then identifies the member performing the request and must be an active member of the organization. client_id: The OAuth client ID to store for this integration. client_secret: The OAuth client secret to store for this integration. config: Provider-specific configuration values collected for this installation, keyed by the provider's config field descriptors. + connection_intent: Must be `add`: this endpoint only creates another connection. The first connection for an owner shape fills the compatibility slot; later connections are standard. Creating an additional connection is not yet available: until it is, `add` succeeds only when the owner has no connection for this integration and otherwise returns 404 `multiple_connections_unavailable`. + idempotency_key: Optional idempotency key for safe retries. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: - ConnectedAccount + ConnectedAccountWriteResponse Raises: BadRequestError: If the request is malformed (400). @@ -462,7 +520,6 @@ def update_data_integration_client_credentials( AuthorizationError: If the request is forbidden (403). NotFoundError: If the resource is not found (404). ConflictError: If a conflict occurs (409). - UnprocessableEntityError: If the request data is unprocessable (422). RateLimitExceededError: If rate limited (429). ServerError: If the server returns a 5xx error. """ @@ -471,21 +528,67 @@ def update_data_integration_client_credentials( for k, v in { "user_id": user_id, "organization_id": organization_id, - "connected_account_id": connected_account_id, "connection_owner": enum_value(connection_owner) if connection_owner is not None else None, "client_id": client_id, "client_secret": client_secret, "config": config, + "connection_intent": connection_intent, }.items() if v is not None } + if not isinstance(account_display_name, NotGiven): + body["account_display_name"] = account_display_name + if not isinstance(account_identifier, NotGiven): + body["account_identifier"] = account_identifier return self._client.request( - method="put", + method="post", path=("data-integrations", str(slug), "client-credentials"), body=body, - model=ConnectedAccount, + model=ConnectedAccountWriteResponse, + idempotency_key=idempotency_key, + request_options=request_options, + ) + + def update_data_integration_client_credentials( + self, + slug: str, + *, + body: DataIntegrationsUpsertClientCredentialsRequest + | ReauthorizeDataIntegrationsUpsertClientCredentialsRequest + | DataIntegrationsUpsertClientCredentialsRequest2 + | dict[str, Any], + request_options: RequestOptions | None = None, + ) -> ConnectedAccountWriteResponse: + """Upsert client credentials for a connected account + + Creates or updates a client-credentials-based installation for the specified integration, owned by the user or, when `connection_owner` is `organization`, shared by the organization. If an installation already exists, the stored client credentials are rotated to the new values. To create another connection, use POST. + + Args: + slug: The identifier of the integration. + body: The request body. Accepts: DataIntegrationsUpsertClientCredentialsRequest, ReauthorizeDataIntegrationsUpsertClientCredentialsRequest, DataIntegrationsUpsertClientCredentialsRequest2, or a plain dict. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + ConnectedAccountWriteResponse + + Raises: + BadRequestError: If the request is malformed (400). + AuthenticationError: If the API key is invalid (401). + AuthorizationError: If the request is forbidden (403). + NotFoundError: If the resource is not found (404). + ConflictError: If a conflict occurs (409). + UnprocessableEntityError: If the request data is unprocessable (422). + RateLimitExceededError: If rate limited (429). + ServerError: If the server returns a 5xx error. + """ + _body: dict[str, Any] = body if isinstance(body, dict) else body.to_dict() + return self._client.request( + method="put", + path=("data-integrations", str(slug), "client-credentials"), + body=_body, + model=ConnectedAccountWriteResponse, request_options=request_options, ) @@ -504,7 +607,7 @@ def create_data_integration_credential( ) -> DataIntegrationCredentialsResponse: """Vend credentials for a connected account - Returns credentials for a user's connected account. Branches on the installation's `auth_method`: OAuth installations return an access token (refreshed if needed); API-key installations return the stored secret. + Returns credentials for a user's connected account. Branches on the installation's `auth_method`: OAuth installations return an access token (refreshed if needed); API-key installations return the stored secret. Every active credential includes `config`: provider-declared, non-secret values from the installation snapshot, with current provider defaults for unset fields. Editing integration or organization configuration does not change the snapshot; reconnect or explicitly rebind the connection to adopt those edits. Defaults remain live, so a changed default can appear in `config` before a cached token is refreshed or re-minted. Credentials that never refresh require a reconnect or rebind when a default changes their routing. Args: slug: The identifier of the integration. @@ -788,32 +891,43 @@ def create_organization_connected_account( organization_id: str, slug: str, *, + user_id: str, + account_display_name: str | None | NotGiven = NOT_GIVEN, + account_identifier: str | None | NotGiven = NOT_GIVEN, access_token: str | None = None, refresh_token: str | None = None, expires_at: str | None = None, scopes: list[str] | None = None, - state: ConnectedAccountInputState | str | None = None, + state: CreateOrganizationConnectedAccountState | str | None = None, + connection_intent: Literal["add"] | None = None, + idempotency_key: str | None = None, request_options: RequestOptions | None = None, - ) -> ConnectedAccount: + ) -> ConnectedAccountWriteResponse: """Import an organization connected account - Imports an organization-owned [connected account](https://workos.com/docs/reference/pipes/connected-account) by providing OAuth tokens directly. Use this to migrate existing connections or set up connections without going through the OAuth flow. + Imports an organization-owned [connected account](https://workos.com/docs/reference/pipes/connected-account) by providing OAuth tokens directly. Omit `connection_intent` to create only the compatibility connection, or set it to `add` to explicitly create another connection. This creation-only endpoint does not accept `connected_account_id` or reauthorization intent. Args: organization_id: An [Organization](https://workos.com/docs/reference/organization) identifier. slug: The slug identifier of the provider (e.g., `github`, `slack`, `notion`). + account_display_name: A mutable, non-unique connection label; null clears it. Not an account identifier. + account_identifier: A developer-attested account identifier; null clears it. access_token: The OAuth access token for the connected account. refresh_token: The OAuth refresh token for the connected account. expires_at: The ISO-8601 timestamp when the access token expires. Required when `access_token` is provided for tokens that expire. scopes: The OAuth scopes granted for this connection. state: Explicitly set the state of the connected account. When omitted, the state is derived from the token combination provided. + user_id: The [User](https://workos.com/docs/reference/authkit/user) identifier of the organization member on whose behalf the connected account is being imported or updated. The user must be an active member of the organization. + connection_intent: Set to `add` to create another connected account. Omit this field for permanent compatibility behavior. Creating an additional connection is not yet available: until it is, `add` succeeds only when the owner has no connection for this integration, which creates the compatibility connection, and otherwise returns 404 `multiple_connections_unavailable`. + idempotency_key: Optional idempotency key for safe retries. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: - ConnectedAccount + ConnectedAccountWriteResponse Raises: AuthenticationError: If the API key is invalid (401). + AuthorizationError: If the request is forbidden (403). NotFoundError: If the resource is not found (404). ConflictError: If a conflict occurs (409). UnprocessableEntityError: If the request data is unprocessable (422). @@ -828,9 +942,15 @@ def create_organization_connected_account( "expires_at": expires_at, "scopes": scopes, "state": enum_value(state) if state is not None else None, + "user_id": user_id, + "connection_intent": connection_intent, }.items() if v is not None } + if not isinstance(account_display_name, NotGiven): + body["account_display_name"] = account_display_name + if not isinstance(account_identifier, NotGiven): + body["account_identifier"] = account_identifier return self._client.request( method="post", path=( @@ -840,7 +960,8 @@ def create_organization_connected_account( str(slug), ), body=body, - model=ConnectedAccount, + model=ConnectedAccountWriteResponse, + idempotency_key=idempotency_key, request_options=request_options, ) @@ -849,15 +970,19 @@ def update_organization_connected_account( organization_id: str, slug: str, *, + user_id: str, + account_display_name: str | None | NotGiven = NOT_GIVEN, + account_identifier: str | None | NotGiven = NOT_GIVEN, access_token: str | None = None, refresh_token: str | None = None, expires_at: str | None = None, scopes: list[str] | None = None, - state: ConnectedAccountInputState | str | None = None, + state: OrganizationConnectedAccountState | str | None = None, supports_multiple_connections: bool | None = None, connected_account_id: str | None = None, + connection_intent: Literal["reauthorize"] | None = None, request_options: RequestOptions | None = None, - ) -> ConnectedAccount: + ) -> ConnectedAccountWriteResponse: """Update an organization connected account Updates an organization's [connected account](https://workos.com/docs/reference/pipes/connected-account) tokens, scopes, or state for a specific provider. @@ -865,21 +990,26 @@ def update_organization_connected_account( Args: organization_id: An [Organization](https://workos.com/docs/reference/organization) identifier. slug: The slug identifier of the provider (e.g., `github`, `slack`, `notion`). + account_display_name: A mutable, non-unique connection label; null clears it. Not an account identifier. + account_identifier: A developer-attested account identifier; null clears it. access_token: The OAuth access token for the connected account. refresh_token: The OAuth refresh token for the connected account. expires_at: The ISO-8601 timestamp when the access token expires. Required when `access_token` is provided for tokens that expire. scopes: The OAuth scopes granted for this connection. state: Explicitly set the state of the connected account. When omitted, the state is derived from the token combination provided. - supports_multiple_connections: Set to `true` to use the plural connection contract. When omitted or `false`, only the compatibility connection is considered. + user_id: The [User](https://workos.com/docs/reference/authkit/user) identifier of the organization member on whose behalf the connected account is being imported or updated. The user must be an active member of the organization. + supports_multiple_connections: Accepted for compatibility; does not change update targeting. Omit intent and selector to update the compatibility connection, or supply `connected_account_id` to update an exact connection. connected_account_id: A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to select the connection to update. + connection_intent: Set to `reauthorize` with `connected_account_id` to update one exact connection. The intent may be omitted when supplying an ID. Omit both for permanent compatibility behavior. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: - ConnectedAccount + ConnectedAccountWriteResponse Raises: BadRequestError: If the request is malformed (400). AuthenticationError: If the API key is invalid (401). + AuthorizationError: If the request is forbidden (403). NotFoundError: If the resource is not found (404). ConflictError: If a conflict occurs (409). UnprocessableEntityError: If the request data is unprocessable (422). @@ -894,14 +1024,20 @@ def update_organization_connected_account( "expires_at": expires_at, "scopes": scopes, "state": enum_value(state) if state is not None else None, + "user_id": user_id, }.items() if v is not None } + if not isinstance(account_display_name, NotGiven): + body["account_display_name"] = account_display_name + if not isinstance(account_identifier, NotGiven): + body["account_identifier"] = account_identifier params: dict[str, Any] = { k: v for k, v in { "supports_multiple_connections": supports_multiple_connections, "connected_account_id": connected_account_id, + "connection_intent": connection_intent, }.items() if v is not None } @@ -915,7 +1051,7 @@ def update_organization_connected_account( ), body=body, params=params, - model=ConnectedAccount, + model=ConnectedAccountWriteResponse, request_options=request_options, ) @@ -1068,14 +1204,18 @@ def create_user_connected_account( user_id: str, slug: str, *, + account_display_name: str | None | NotGiven = NOT_GIVEN, + account_identifier: str | None | NotGiven = NOT_GIVEN, access_token: str | None = None, refresh_token: str | None = None, expires_at: str | None = None, scopes: list[str] | None = None, - state: ConnectedAccountInputState | str | None = None, + state: CreateConnectedAccountState | str | None = None, + connection_intent: Literal["add"] | None = None, organization_id: str | None = None, + idempotency_key: str | None = None, request_options: RequestOptions | None = None, - ) -> ConnectedAccount: + ) -> ConnectedAccountWriteResponse: """Import a connected account Imports a [connected account](https://workos.com/docs/reference/pipes/connected-account) for a user by providing OAuth tokens directly. Use this to migrate existing connections or set up connections without going through the OAuth flow. @@ -1083,16 +1223,20 @@ def create_user_connected_account( Args: user_id: A [User](https://workos.com/docs/reference/authkit/user) identifier. slug: The slug identifier of the provider (e.g., `github`, `slack`, `notion`). + account_display_name: A mutable, non-unique connection label; null clears it. Not an account identifier. + account_identifier: A developer-attested account identifier; null clears it. access_token: The OAuth access token for the connected account. refresh_token: The OAuth refresh token for the connected account. expires_at: The ISO-8601 timestamp when the access token expires. Required when `access_token` is provided for tokens that expire. scopes: The OAuth scopes granted for this connection. state: Explicitly set the state of the connected account. When omitted, the state is derived from the token combination provided. + connection_intent: Set to `add` to create another connected account. Omit this field for permanent compatibility behavior. Creating an additional connection is not yet available: until it is, `add` succeeds only when the owner has no connection for this integration, which creates the compatibility connection, and otherwise returns 404 `multiple_connections_unavailable`. organization_id: An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter if the connection is scoped to an organization. + idempotency_key: Optional idempotency key for safe retries. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: - ConnectedAccount + ConnectedAccountWriteResponse Raises: AuthenticationError: If the API key is invalid (401). @@ -1110,9 +1254,14 @@ def create_user_connected_account( "expires_at": expires_at, "scopes": scopes, "state": enum_value(state) if state is not None else None, + "connection_intent": connection_intent, }.items() if v is not None } + if not isinstance(account_display_name, NotGiven): + body["account_display_name"] = account_display_name + if not isinstance(account_identifier, NotGiven): + body["account_identifier"] = account_identifier params: dict[str, Any] = { k: v for k, v in { @@ -1131,7 +1280,8 @@ def create_user_connected_account( ), body=body, params=params, - model=ConnectedAccount, + model=ConnectedAccountWriteResponse, + idempotency_key=idempotency_key, request_options=request_options, ) @@ -1140,6 +1290,8 @@ def update_user_connected_account( user_id: str, slug: str, *, + account_display_name: str | None | NotGiven = NOT_GIVEN, + account_identifier: str | None | NotGiven = NOT_GIVEN, access_token: str | None = None, refresh_token: str | None = None, expires_at: str | None = None, @@ -1148,8 +1300,9 @@ def update_user_connected_account( organization_id: str | None = None, supports_multiple_connections: bool | None = None, connected_account_id: str | None = None, + connection_intent: Literal["reauthorize"] | None = None, request_options: RequestOptions | None = None, - ) -> ConnectedAccount: + ) -> ConnectedAccountWriteResponse: """Update a connected account Updates a user's [connected account](https://workos.com/docs/reference/pipes/connected-account) tokens, scopes, or state for a specific provider. @@ -1157,18 +1310,21 @@ def update_user_connected_account( Args: user_id: A [User](https://workos.com/docs/reference/authkit/user) identifier. slug: The slug identifier of the provider (e.g., `github`, `slack`, `notion`). + account_display_name: A mutable, non-unique connection label; null clears it. Not an account identifier. + account_identifier: A developer-attested account identifier; null clears it. access_token: The OAuth access token for the connected account. refresh_token: The OAuth refresh token for the connected account. expires_at: The ISO-8601 timestamp when the access token expires. Required when `access_token` is provided for tokens that expire. scopes: The OAuth scopes granted for this connection. state: Explicitly set the state of the connected account. When omitted, the state is derived from the token combination provided. organization_id: An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter if the connection is scoped to an organization. - supports_multiple_connections: Set to `true` to use the plural connection contract. When omitted or `false`, only the compatibility connection is considered. + supports_multiple_connections: Accepted for compatibility; does not change update targeting. Omit intent and selector to update the compatibility connection, or supply `connected_account_id` to update an exact connection. connected_account_id: A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to select the connection to update. + connection_intent: Set to `reauthorize` with `connected_account_id` to update one exact connection. The intent may be omitted when supplying an ID. Omit both for permanent compatibility behavior. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: - ConnectedAccount + ConnectedAccountWriteResponse Raises: BadRequestError: If the request is malformed (400). @@ -1190,12 +1346,17 @@ def update_user_connected_account( }.items() if v is not None } + if not isinstance(account_display_name, NotGiven): + body["account_display_name"] = account_display_name + if not isinstance(account_identifier, NotGiven): + body["account_identifier"] = account_identifier params: dict[str, Any] = { k: v for k, v in { "organization_id": organization_id, "supports_multiple_connections": supports_multiple_connections, "connected_account_id": connected_account_id, + "connection_intent": connection_intent, }.items() if v is not None } @@ -1210,7 +1371,7 @@ def update_user_connected_account( ), body=body, params=params, - model=ConnectedAccount, + model=ConnectedAccountWriteResponse, request_options=request_options, ) @@ -1563,34 +1724,40 @@ async def delete_data_integration( request_options=request_options, ) - async def update_data_integration_api_key( + async def create_data_integration_api_key( self, slug: str, *, user_id: str, secret: str, + connection_intent: Literal["add"], + account_display_name: str | None | NotGiven = NOT_GIVEN, + account_identifier: str | None | NotGiven = NOT_GIVEN, organization_id: str | None = None, - connected_account_id: str | None = None, - connection_owner: DataIntegrationsUpsertApiKeyRequestConnectionOwner + connection_owner: DataIntegrationsCreateApiKeyConnectionRequestConnectionOwner | str | None = None, + idempotency_key: str | None = None, request_options: RequestOptions | None = None, - ) -> ConnectedAccount: - """Upsert an API key for a connected account + ) -> ConnectedAccountWriteResponse: + """Create another API key connected account - Creates or updates an API-key-based installation for the specified integration, owned by the user or, when `connection_owner` is `organization`, shared by the organization. If an installation already exists, the stored API key is rotated to the new value. + Creates another API key-based connected account for the specified integration, owned by the user or, when `connection_owner` is `organization`, shared by the organization. Requires `connection_intent: add` and does not accept `connected_account_id`; use PUT to create or rotate the compatibility connection or to update an exact connection. Creating an additional connection is not yet available: until it is, this endpoint succeeds only when the owner has no connection for this integration, which creates the compatibility connection, and otherwise returns 404 `multiple_connections_unavailable`. Args: slug: The identifier of the integration. + account_display_name: A mutable, non-unique label. Omit to preserve an existing label; null clears it. Labels are never account identifiers. + account_identifier: A developer-attested provider account identifier, never inferred from a label or credential. Omit to preserve an existing identifier; null clears it. user_id: A [User](https://workos.com/docs/reference/authkit/user) identifier. organization_id: An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to scope the connection to a specific organization. Required when `connection_owner` is `organization`. - connected_account_id: A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to rotate a specific existing connection. connection_owner: Whose connection to create or rotate. `user` (the default) addresses the connection owned by `user_id`. `organization` addresses the connection shared by every member of `organization_id`; `user_id` then identifies the member performing the request and must be an active member of the organization. secret: The API key secret to store for this integration. + connection_intent: Must be `add`: this endpoint only creates another connection. The first connection for an owner shape fills the compatibility slot; later connections are standard. Creating an additional connection is not yet available: until it is, `add` succeeds only when the owner has no connection for this integration and otherwise returns 404 `multiple_connections_unavailable`. + idempotency_key: Optional idempotency key for safe retries. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: - ConnectedAccount + ConnectedAccountWriteResponse Raises: BadRequestError: If the request is malformed (400). @@ -1598,7 +1765,6 @@ async def update_data_integration_api_key( AuthorizationError: If the request is forbidden (403). NotFoundError: If the resource is not found (404). ConflictError: If a conflict occurs (409). - UnprocessableEntityError: If the request data is unprocessable (422). RateLimitExceededError: If rate limited (429). ServerError: If the server returns a 5xx error. """ @@ -1607,19 +1773,65 @@ async def update_data_integration_api_key( for k, v in { "user_id": user_id, "organization_id": organization_id, - "connected_account_id": connected_account_id, "connection_owner": enum_value(connection_owner) if connection_owner is not None else None, "secret": secret, + "connection_intent": connection_intent, }.items() if v is not None } + if not isinstance(account_display_name, NotGiven): + body["account_display_name"] = account_display_name + if not isinstance(account_identifier, NotGiven): + body["account_identifier"] = account_identifier return await self._client.request( - method="put", + method="post", path=("data-integrations", str(slug), "api-key"), body=body, - model=ConnectedAccount, + model=ConnectedAccountWriteResponse, + idempotency_key=idempotency_key, + request_options=request_options, + ) + + async def update_data_integration_api_key( + self, + slug: str, + *, + body: DataIntegrationsUpsertApiKeyRequest + | ReauthorizeDataIntegrationsUpsertApiKeyRequest + | DataIntegrationsUpsertApiKeyRequest2 + | dict[str, Any], + request_options: RequestOptions | None = None, + ) -> ConnectedAccountWriteResponse: + """Upsert an API key for a connected account + + Creates or updates an API-key-based installation for the specified integration, owned by the user or, when `connection_owner` is `organization`, shared by the organization. If an installation already exists, the stored API key is rotated to the new value. To create another connection, use POST. + + Args: + slug: The identifier of the integration. + body: The request body. Accepts: DataIntegrationsUpsertApiKeyRequest, ReauthorizeDataIntegrationsUpsertApiKeyRequest, DataIntegrationsUpsertApiKeyRequest2, or a plain dict. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + ConnectedAccountWriteResponse + + Raises: + BadRequestError: If the request is malformed (400). + AuthenticationError: If the API key is invalid (401). + AuthorizationError: If the request is forbidden (403). + NotFoundError: If the resource is not found (404). + ConflictError: If a conflict occurs (409). + UnprocessableEntityError: If the request data is unprocessable (422). + RateLimitExceededError: If rate limited (429). + ServerError: If the server returns a 5xx error. + """ + _body: dict[str, Any] = body if isinstance(body, dict) else body.to_dict() + return await self._client.request( + method="put", + path=("data-integrations", str(slug), "api-key"), + body=_body, + model=ConnectedAccountWriteResponse, request_options=request_options, ) @@ -1627,13 +1839,10 @@ async def authorize_data_integration( self, slug: str, *, - user_id: str, - organization_id: str | None = None, - connection_owner: DataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner - | str - | None = None, - return_to: str | None = None, - config: dict[str, str] | None = None, + body: DataIntegrationsGetDataIntegrationAuthorizeUrlRequest + | AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequest + | ReauthorizeDataIntegrationsGetDataIntegrationAuthorizeUrlRequest + | dict[str, Any], request_options: RequestOptions | None = None, ) -> DataIntegrationAuthorizeUrlResponse: """Get authorization URL @@ -1642,11 +1851,7 @@ async def authorize_data_integration( Args: slug: The slug identifier of the provider (e.g., `github`, `slack`, `notion`). - user_id: The ID of the user to authorize. When `connection_owner` is `organization`, this is the user authorizing on behalf of the organization; they must be an active member of the organization and do not become the owner of the resulting connected account. - organization_id: An organization ID to scope the authorization to a specific organization. Required when `connection_owner` is `organization`. - connection_owner: Who will own the connected account. `user` (the default) connects the user's own account. `organization` connects the organization's shared account and requires `organization_id`. - return_to: The URL to redirect the user to after authorization. - config: Connect-time config values for the provider-declared `installation`-scope fields (e.g. a Zendesk `subdomain`), keyed by the config field. Only fields the provider declares may be supplied, and required fields must be provided unless already pinned on the integration. + body: The request body. Accepts: DataIntegrationsGetDataIntegrationAuthorizeUrlRequest, AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequest, ReauthorizeDataIntegrationsGetDataIntegrationAuthorizeUrlRequest, or a plain dict. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -1657,62 +1862,58 @@ async def authorize_data_integration( AuthenticationError: If the API key is invalid (401). AuthorizationError: If the request is forbidden (403). NotFoundError: If the resource is not found (404). + ConflictError: If a conflict occurs (409). + UnprocessableEntityError: If the request data is unprocessable (422). RateLimitExceededError: If rate limited (429). ServerError: If the server returns a 5xx error. """ - body: dict[str, Any] = { - k: v - for k, v in { - "user_id": user_id, - "organization_id": organization_id, - "connection_owner": enum_value(connection_owner) - if connection_owner is not None - else None, - "return_to": return_to, - "config": config, - }.items() - if v is not None - } + _body: dict[str, Any] = body if isinstance(body, dict) else body.to_dict() return await self._client.request( method="post", path=("data-integrations", str(slug), "authorize"), - body=body, + body=_body, model=DataIntegrationAuthorizeUrlResponse, request_options=request_options, ) - async def update_data_integration_client_credentials( + async def create_data_integration_client_credential( self, slug: str, *, user_id: str, client_id: str, client_secret: str, + connection_intent: Literal["add"], + account_display_name: str | None | NotGiven = NOT_GIVEN, + account_identifier: str | None | NotGiven = NOT_GIVEN, organization_id: str | None = None, - connected_account_id: str | None = None, - connection_owner: DataIntegrationsUpsertClientCredentialsRequestConnectionOwner + connection_owner: DataIntegrationsCreateClientCredentialsConnectionRequestConnectionOwner | str | None = None, config: dict[str, str] | None = None, + idempotency_key: str | None = None, request_options: RequestOptions | None = None, - ) -> ConnectedAccount: - """Upsert client credentials for a connected account + ) -> ConnectedAccountWriteResponse: + """Create another client credentials connected account - Creates or updates a client-credentials-based installation for the specified integration, owned by the user or, when `connection_owner` is `organization`, shared by the organization. If an installation already exists, the stored client credentials are rotated to the new values. + Creates another client credentials-based connected account for the specified integration, owned by the user or, when `connection_owner` is `organization`, shared by the organization. Requires `connection_intent: add` and does not accept `connected_account_id`; use PUT to create or rotate the compatibility connection or to update an exact connection. Creating an additional connection is not yet available: until it is, this endpoint succeeds only when the owner has no connection for this integration, which creates the compatibility connection, and otherwise returns 404 `multiple_connections_unavailable`. Args: slug: The identifier of the integration. + account_display_name: A mutable, non-unique label. Omit to preserve an existing label; null clears it. Labels are never account identifiers. + account_identifier: A developer-attested provider account identifier, never inferred from a label or credential. Omit to preserve an existing identifier; null clears it. user_id: A [User](https://workos.com/docs/reference/authkit/user) identifier. organization_id: An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to scope the connection to a specific organization. Required when `connection_owner` is `organization`. - connected_account_id: A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to rotate a specific existing connection. connection_owner: Whose connection to create or rotate. `user` (the default) addresses the connection owned by `user_id`. `organization` addresses the connection shared by every member of `organization_id`; `user_id` then identifies the member performing the request and must be an active member of the organization. client_id: The OAuth client ID to store for this integration. client_secret: The OAuth client secret to store for this integration. config: Provider-specific configuration values collected for this installation, keyed by the provider's config field descriptors. + connection_intent: Must be `add`: this endpoint only creates another connection. The first connection for an owner shape fills the compatibility slot; later connections are standard. Creating an additional connection is not yet available: until it is, `add` succeeds only when the owner has no connection for this integration and otherwise returns 404 `multiple_connections_unavailable`. + idempotency_key: Optional idempotency key for safe retries. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: - ConnectedAccount + ConnectedAccountWriteResponse Raises: BadRequestError: If the request is malformed (400). @@ -1720,7 +1921,6 @@ async def update_data_integration_client_credentials( AuthorizationError: If the request is forbidden (403). NotFoundError: If the resource is not found (404). ConflictError: If a conflict occurs (409). - UnprocessableEntityError: If the request data is unprocessable (422). RateLimitExceededError: If rate limited (429). ServerError: If the server returns a 5xx error. """ @@ -1729,21 +1929,67 @@ async def update_data_integration_client_credentials( for k, v in { "user_id": user_id, "organization_id": organization_id, - "connected_account_id": connected_account_id, "connection_owner": enum_value(connection_owner) if connection_owner is not None else None, "client_id": client_id, "client_secret": client_secret, "config": config, + "connection_intent": connection_intent, }.items() if v is not None } + if not isinstance(account_display_name, NotGiven): + body["account_display_name"] = account_display_name + if not isinstance(account_identifier, NotGiven): + body["account_identifier"] = account_identifier return await self._client.request( - method="put", + method="post", path=("data-integrations", str(slug), "client-credentials"), body=body, - model=ConnectedAccount, + model=ConnectedAccountWriteResponse, + idempotency_key=idempotency_key, + request_options=request_options, + ) + + async def update_data_integration_client_credentials( + self, + slug: str, + *, + body: DataIntegrationsUpsertClientCredentialsRequest + | ReauthorizeDataIntegrationsUpsertClientCredentialsRequest + | DataIntegrationsUpsertClientCredentialsRequest2 + | dict[str, Any], + request_options: RequestOptions | None = None, + ) -> ConnectedAccountWriteResponse: + """Upsert client credentials for a connected account + + Creates or updates a client-credentials-based installation for the specified integration, owned by the user or, when `connection_owner` is `organization`, shared by the organization. If an installation already exists, the stored client credentials are rotated to the new values. To create another connection, use POST. + + Args: + slug: The identifier of the integration. + body: The request body. Accepts: DataIntegrationsUpsertClientCredentialsRequest, ReauthorizeDataIntegrationsUpsertClientCredentialsRequest, DataIntegrationsUpsertClientCredentialsRequest2, or a plain dict. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + ConnectedAccountWriteResponse + + Raises: + BadRequestError: If the request is malformed (400). + AuthenticationError: If the API key is invalid (401). + AuthorizationError: If the request is forbidden (403). + NotFoundError: If the resource is not found (404). + ConflictError: If a conflict occurs (409). + UnprocessableEntityError: If the request data is unprocessable (422). + RateLimitExceededError: If rate limited (429). + ServerError: If the server returns a 5xx error. + """ + _body: dict[str, Any] = body if isinstance(body, dict) else body.to_dict() + return await self._client.request( + method="put", + path=("data-integrations", str(slug), "client-credentials"), + body=_body, + model=ConnectedAccountWriteResponse, request_options=request_options, ) @@ -1762,7 +2008,7 @@ async def create_data_integration_credential( ) -> DataIntegrationCredentialsResponse: """Vend credentials for a connected account - Returns credentials for a user's connected account. Branches on the installation's `auth_method`: OAuth installations return an access token (refreshed if needed); API-key installations return the stored secret. + Returns credentials for a user's connected account. Branches on the installation's `auth_method`: OAuth installations return an access token (refreshed if needed); API-key installations return the stored secret. Every active credential includes `config`: provider-declared, non-secret values from the installation snapshot, with current provider defaults for unset fields. Editing integration or organization configuration does not change the snapshot; reconnect or explicitly rebind the connection to adopt those edits. Defaults remain live, so a changed default can appear in `config` before a cached token is refreshed or re-minted. Credentials that never refresh require a reconnect or rebind when a default changes their routing. Args: slug: The identifier of the integration. @@ -2046,32 +2292,43 @@ async def create_organization_connected_account( organization_id: str, slug: str, *, + user_id: str, + account_display_name: str | None | NotGiven = NOT_GIVEN, + account_identifier: str | None | NotGiven = NOT_GIVEN, access_token: str | None = None, refresh_token: str | None = None, expires_at: str | None = None, scopes: list[str] | None = None, - state: ConnectedAccountInputState | str | None = None, + state: CreateOrganizationConnectedAccountState | str | None = None, + connection_intent: Literal["add"] | None = None, + idempotency_key: str | None = None, request_options: RequestOptions | None = None, - ) -> ConnectedAccount: + ) -> ConnectedAccountWriteResponse: """Import an organization connected account - Imports an organization-owned [connected account](https://workos.com/docs/reference/pipes/connected-account) by providing OAuth tokens directly. Use this to migrate existing connections or set up connections without going through the OAuth flow. + Imports an organization-owned [connected account](https://workos.com/docs/reference/pipes/connected-account) by providing OAuth tokens directly. Omit `connection_intent` to create only the compatibility connection, or set it to `add` to explicitly create another connection. This creation-only endpoint does not accept `connected_account_id` or reauthorization intent. Args: organization_id: An [Organization](https://workos.com/docs/reference/organization) identifier. slug: The slug identifier of the provider (e.g., `github`, `slack`, `notion`). + account_display_name: A mutable, non-unique connection label; null clears it. Not an account identifier. + account_identifier: A developer-attested account identifier; null clears it. access_token: The OAuth access token for the connected account. refresh_token: The OAuth refresh token for the connected account. expires_at: The ISO-8601 timestamp when the access token expires. Required when `access_token` is provided for tokens that expire. scopes: The OAuth scopes granted for this connection. state: Explicitly set the state of the connected account. When omitted, the state is derived from the token combination provided. + user_id: The [User](https://workos.com/docs/reference/authkit/user) identifier of the organization member on whose behalf the connected account is being imported or updated. The user must be an active member of the organization. + connection_intent: Set to `add` to create another connected account. Omit this field for permanent compatibility behavior. Creating an additional connection is not yet available: until it is, `add` succeeds only when the owner has no connection for this integration, which creates the compatibility connection, and otherwise returns 404 `multiple_connections_unavailable`. + idempotency_key: Optional idempotency key for safe retries. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: - ConnectedAccount + ConnectedAccountWriteResponse Raises: AuthenticationError: If the API key is invalid (401). + AuthorizationError: If the request is forbidden (403). NotFoundError: If the resource is not found (404). ConflictError: If a conflict occurs (409). UnprocessableEntityError: If the request data is unprocessable (422). @@ -2086,9 +2343,15 @@ async def create_organization_connected_account( "expires_at": expires_at, "scopes": scopes, "state": enum_value(state) if state is not None else None, + "user_id": user_id, + "connection_intent": connection_intent, }.items() if v is not None } + if not isinstance(account_display_name, NotGiven): + body["account_display_name"] = account_display_name + if not isinstance(account_identifier, NotGiven): + body["account_identifier"] = account_identifier return await self._client.request( method="post", path=( @@ -2098,7 +2361,8 @@ async def create_organization_connected_account( str(slug), ), body=body, - model=ConnectedAccount, + model=ConnectedAccountWriteResponse, + idempotency_key=idempotency_key, request_options=request_options, ) @@ -2107,15 +2371,19 @@ async def update_organization_connected_account( organization_id: str, slug: str, *, + user_id: str, + account_display_name: str | None | NotGiven = NOT_GIVEN, + account_identifier: str | None | NotGiven = NOT_GIVEN, access_token: str | None = None, refresh_token: str | None = None, expires_at: str | None = None, scopes: list[str] | None = None, - state: ConnectedAccountInputState | str | None = None, + state: OrganizationConnectedAccountState | str | None = None, supports_multiple_connections: bool | None = None, connected_account_id: str | None = None, + connection_intent: Literal["reauthorize"] | None = None, request_options: RequestOptions | None = None, - ) -> ConnectedAccount: + ) -> ConnectedAccountWriteResponse: """Update an organization connected account Updates an organization's [connected account](https://workos.com/docs/reference/pipes/connected-account) tokens, scopes, or state for a specific provider. @@ -2123,21 +2391,26 @@ async def update_organization_connected_account( Args: organization_id: An [Organization](https://workos.com/docs/reference/organization) identifier. slug: The slug identifier of the provider (e.g., `github`, `slack`, `notion`). + account_display_name: A mutable, non-unique connection label; null clears it. Not an account identifier. + account_identifier: A developer-attested account identifier; null clears it. access_token: The OAuth access token for the connected account. refresh_token: The OAuth refresh token for the connected account. expires_at: The ISO-8601 timestamp when the access token expires. Required when `access_token` is provided for tokens that expire. scopes: The OAuth scopes granted for this connection. state: Explicitly set the state of the connected account. When omitted, the state is derived from the token combination provided. - supports_multiple_connections: Set to `true` to use the plural connection contract. When omitted or `false`, only the compatibility connection is considered. + user_id: The [User](https://workos.com/docs/reference/authkit/user) identifier of the organization member on whose behalf the connected account is being imported or updated. The user must be an active member of the organization. + supports_multiple_connections: Accepted for compatibility; does not change update targeting. Omit intent and selector to update the compatibility connection, or supply `connected_account_id` to update an exact connection. connected_account_id: A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to select the connection to update. + connection_intent: Set to `reauthorize` with `connected_account_id` to update one exact connection. The intent may be omitted when supplying an ID. Omit both for permanent compatibility behavior. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: - ConnectedAccount + ConnectedAccountWriteResponse Raises: BadRequestError: If the request is malformed (400). AuthenticationError: If the API key is invalid (401). + AuthorizationError: If the request is forbidden (403). NotFoundError: If the resource is not found (404). ConflictError: If a conflict occurs (409). UnprocessableEntityError: If the request data is unprocessable (422). @@ -2152,14 +2425,20 @@ async def update_organization_connected_account( "expires_at": expires_at, "scopes": scopes, "state": enum_value(state) if state is not None else None, + "user_id": user_id, }.items() if v is not None } + if not isinstance(account_display_name, NotGiven): + body["account_display_name"] = account_display_name + if not isinstance(account_identifier, NotGiven): + body["account_identifier"] = account_identifier params: dict[str, Any] = { k: v for k, v in { "supports_multiple_connections": supports_multiple_connections, "connected_account_id": connected_account_id, + "connection_intent": connection_intent, }.items() if v is not None } @@ -2173,7 +2452,7 @@ async def update_organization_connected_account( ), body=body, params=params, - model=ConnectedAccount, + model=ConnectedAccountWriteResponse, request_options=request_options, ) @@ -2326,14 +2605,18 @@ async def create_user_connected_account( user_id: str, slug: str, *, + account_display_name: str | None | NotGiven = NOT_GIVEN, + account_identifier: str | None | NotGiven = NOT_GIVEN, access_token: str | None = None, refresh_token: str | None = None, expires_at: str | None = None, scopes: list[str] | None = None, - state: ConnectedAccountInputState | str | None = None, + state: CreateConnectedAccountState | str | None = None, + connection_intent: Literal["add"] | None = None, organization_id: str | None = None, + idempotency_key: str | None = None, request_options: RequestOptions | None = None, - ) -> ConnectedAccount: + ) -> ConnectedAccountWriteResponse: """Import a connected account Imports a [connected account](https://workos.com/docs/reference/pipes/connected-account) for a user by providing OAuth tokens directly. Use this to migrate existing connections or set up connections without going through the OAuth flow. @@ -2341,16 +2624,20 @@ async def create_user_connected_account( Args: user_id: A [User](https://workos.com/docs/reference/authkit/user) identifier. slug: The slug identifier of the provider (e.g., `github`, `slack`, `notion`). + account_display_name: A mutable, non-unique connection label; null clears it. Not an account identifier. + account_identifier: A developer-attested account identifier; null clears it. access_token: The OAuth access token for the connected account. refresh_token: The OAuth refresh token for the connected account. expires_at: The ISO-8601 timestamp when the access token expires. Required when `access_token` is provided for tokens that expire. scopes: The OAuth scopes granted for this connection. state: Explicitly set the state of the connected account. When omitted, the state is derived from the token combination provided. + connection_intent: Set to `add` to create another connected account. Omit this field for permanent compatibility behavior. Creating an additional connection is not yet available: until it is, `add` succeeds only when the owner has no connection for this integration, which creates the compatibility connection, and otherwise returns 404 `multiple_connections_unavailable`. organization_id: An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter if the connection is scoped to an organization. + idempotency_key: Optional idempotency key for safe retries. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: - ConnectedAccount + ConnectedAccountWriteResponse Raises: AuthenticationError: If the API key is invalid (401). @@ -2368,9 +2655,14 @@ async def create_user_connected_account( "expires_at": expires_at, "scopes": scopes, "state": enum_value(state) if state is not None else None, + "connection_intent": connection_intent, }.items() if v is not None } + if not isinstance(account_display_name, NotGiven): + body["account_display_name"] = account_display_name + if not isinstance(account_identifier, NotGiven): + body["account_identifier"] = account_identifier params: dict[str, Any] = { k: v for k, v in { @@ -2389,7 +2681,8 @@ async def create_user_connected_account( ), body=body, params=params, - model=ConnectedAccount, + model=ConnectedAccountWriteResponse, + idempotency_key=idempotency_key, request_options=request_options, ) @@ -2398,6 +2691,8 @@ async def update_user_connected_account( user_id: str, slug: str, *, + account_display_name: str | None | NotGiven = NOT_GIVEN, + account_identifier: str | None | NotGiven = NOT_GIVEN, access_token: str | None = None, refresh_token: str | None = None, expires_at: str | None = None, @@ -2406,8 +2701,9 @@ async def update_user_connected_account( organization_id: str | None = None, supports_multiple_connections: bool | None = None, connected_account_id: str | None = None, + connection_intent: Literal["reauthorize"] | None = None, request_options: RequestOptions | None = None, - ) -> ConnectedAccount: + ) -> ConnectedAccountWriteResponse: """Update a connected account Updates a user's [connected account](https://workos.com/docs/reference/pipes/connected-account) tokens, scopes, or state for a specific provider. @@ -2415,18 +2711,21 @@ async def update_user_connected_account( Args: user_id: A [User](https://workos.com/docs/reference/authkit/user) identifier. slug: The slug identifier of the provider (e.g., `github`, `slack`, `notion`). + account_display_name: A mutable, non-unique connection label; null clears it. Not an account identifier. + account_identifier: A developer-attested account identifier; null clears it. access_token: The OAuth access token for the connected account. refresh_token: The OAuth refresh token for the connected account. expires_at: The ISO-8601 timestamp when the access token expires. Required when `access_token` is provided for tokens that expire. scopes: The OAuth scopes granted for this connection. state: Explicitly set the state of the connected account. When omitted, the state is derived from the token combination provided. organization_id: An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter if the connection is scoped to an organization. - supports_multiple_connections: Set to `true` to use the plural connection contract. When omitted or `false`, only the compatibility connection is considered. + supports_multiple_connections: Accepted for compatibility; does not change update targeting. Omit intent and selector to update the compatibility connection, or supply `connected_account_id` to update an exact connection. connected_account_id: A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to select the connection to update. + connection_intent: Set to `reauthorize` with `connected_account_id` to update one exact connection. The intent may be omitted when supplying an ID. Omit both for permanent compatibility behavior. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: - ConnectedAccount + ConnectedAccountWriteResponse Raises: BadRequestError: If the request is malformed (400). @@ -2448,12 +2747,17 @@ async def update_user_connected_account( }.items() if v is not None } + if not isinstance(account_display_name, NotGiven): + body["account_display_name"] = account_display_name + if not isinstance(account_identifier, NotGiven): + body["account_identifier"] = account_identifier params: dict[str, Any] = { k: v for k, v in { "organization_id": organization_id, "supports_multiple_connections": supports_multiple_connections, "connected_account_id": connected_account_id, + "connection_intent": connection_intent, }.items() if v is not None } @@ -2468,7 +2772,7 @@ async def update_user_connected_account( ), body=body, params=params, - model=ConnectedAccount, + model=ConnectedAccountWriteResponse, request_options=request_options, ) diff --git a/src/workos/pipes/models/__init__.py b/src/workos/pipes/models/__init__.py index 29ec1a35..c4050c01 100644 --- a/src/workos/pipes/models/__init__.py +++ b/src/workos/pipes/models/__init__.py @@ -1,6 +1,12 @@ # This file is auto-generated by oagen. Do not edit. from workos.common.models.connected_account import ConnectedAccount as ConnectedAccount +from workos.common.models.connected_account_write_response import ( + ConnectedAccountWriteResponse as ConnectedAccountWriteResponse, +) +from workos.common.models.connected_account_write_response_warning import ( + ConnectedAccountWriteResponseWarning as ConnectedAccountWriteResponseWarning, +) from workos.common.models.data_integrations_list_response import ( DataIntegrationsListResponse as DataIntegrationsListResponse, ) @@ -11,8 +17,16 @@ DataIntegrationsListResponseDataConnectedAccount as DataIntegrationsListResponseDataConnectedAccount, ) +from .add_data_integrations_get_data_integration_authorize_url_request import ( + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequest as AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequest, +) from .api_key_installation import ApiKeyInstallation as ApiKeyInstallation +from .connected_account_input import ConnectedAccountInput as ConnectedAccountInput +from .create_connected_account import CreateConnectedAccount as CreateConnectedAccount from .create_data_integration import CreateDataIntegration as CreateDataIntegration +from .create_organization_connected_account import ( + CreateOrganizationConnectedAccount as CreateOrganizationConnectedAccount, +) from .custom_provider_definition import ( CustomProviderDefinition as CustomProviderDefinition, ) @@ -35,13 +49,21 @@ from .data_integration_credentials_response import ( DataIntegrationCredentialsResponse as DataIntegrationCredentialsResponse, ) -from .data_integration_credentials_response_credential import * from .data_integration_custom_provider import ( DataIntegrationCustomProvider as DataIntegrationCustomProvider, ) from .data_integration_installation import ( DataIntegrationInstallation as DataIntegrationInstallation, ) +from .data_integration_vended_credential import ( + DataIntegrationVendedCredential as DataIntegrationVendedCredential, +) +from .data_integrations_create_api_key_connection_request import ( + DataIntegrationsCreateApiKeyConnectionRequest as DataIntegrationsCreateApiKeyConnectionRequest, +) +from .data_integrations_create_client_credentials_connection_request import ( + DataIntegrationsCreateClientCredentialsConnectionRequest as DataIntegrationsCreateClientCredentialsConnectionRequest, +) from .data_integrations_get_data_integration_authorize_url_request import ( DataIntegrationsGetDataIntegrationAuthorizeUrlRequest as DataIntegrationsGetDataIntegrationAuthorizeUrlRequest, ) @@ -51,13 +73,31 @@ from .data_integrations_upsert_api_key_request import ( DataIntegrationsUpsertApiKeyRequest as DataIntegrationsUpsertApiKeyRequest, ) +from .data_integrations_upsert_api_key_request_2 import ( + DataIntegrationsUpsertApiKeyRequest2 as DataIntegrationsUpsertApiKeyRequest2, +) from .data_integrations_upsert_client_credentials_request import ( DataIntegrationsUpsertClientCredentialsRequest as DataIntegrationsUpsertClientCredentialsRequest, ) +from .data_integrations_upsert_client_credentials_request_2 import ( + DataIntegrationsUpsertClientCredentialsRequest2 as DataIntegrationsUpsertClientCredentialsRequest2, +) from .data_integrations_vend_credentials_request import ( DataIntegrationsVendCredentialsRequest as DataIntegrationsVendCredentialsRequest, ) +from .organization_connected_account import ( + OrganizationConnectedAccount as OrganizationConnectedAccount, +) from .pipes_ownership import PipesOwnership as PipesOwnership +from .reauthorize_data_integrations_get_data_integration_authorize_url_request import ( + ReauthorizeDataIntegrationsGetDataIntegrationAuthorizeUrlRequest as ReauthorizeDataIntegrationsGetDataIntegrationAuthorizeUrlRequest, +) +from .reauthorize_data_integrations_upsert_api_key_request import ( + ReauthorizeDataIntegrationsUpsertApiKeyRequest as ReauthorizeDataIntegrationsUpsertApiKeyRequest, +) +from .reauthorize_data_integrations_upsert_client_credentials_request import ( + ReauthorizeDataIntegrationsUpsertClientCredentialsRequest as ReauthorizeDataIntegrationsUpsertClientCredentialsRequest, +) from .update_custom_provider_definition import ( UpdateCustomProviderDefinition as UpdateCustomProviderDefinition, ) diff --git a/src/workos/pipes/models/add_data_integrations_get_data_integration_authorize_url_request.py b/src/workos/pipes/models/add_data_integrations_get_data_integration_authorize_url_request.py new file mode 100644 index 00000000..53f00d24 --- /dev/null +++ b/src/workos/pipes/models/add_data_integrations_get_data_integration_authorize_url_request.py @@ -0,0 +1,81 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from enum import Enum +from typing import Any, Literal + +from workos._types import _raise_deserialize_error +from workos.common.models.add_data_integrations_get_data_integration_authorize_url_request_connection_owner import ( + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner, +) + + +@dataclass(slots=True) +class AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequest: + """Add Data Integrations Get Data Integration Authorize Url Request model.""" + + user_id: str + """The ID of the user to authorize. When `connection_owner` is `organization`, this is the user authorizing on behalf of the organization; they must be an active member of the organization and do not become the owner of the resulting connected account.""" + connection_intent: Literal["add"] + """Create another connection. The first connection for an owner shape fills the compatibility slot; later connections are standard. Creating an additional connection is not yet available: until it is, `add` succeeds only when the owner has no connection for this integration and otherwise returns 404 `multiple_connections_unavailable`. Omit `connection_intent` to use the compatibility connection, or send `reauthorize` with `connected_account_id` to reauthorize one exact connection.""" + account_display_name: str | None = None + """A mutable, non-unique label. Omit to preserve an existing label; null clears it. Labels are never account identifiers.""" + organization_id: str | None = None + """An organization ID to scope the authorization to a specific organization. Required when `connection_owner` is `organization`.""" + connection_owner: ( + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner | None + ) = None + """Who will own the connected account. `user` (the default) connects the user's own account. `organization` connects the organization's shared account and requires `organization_id`.""" + return_to: str | None = None + """The URL to redirect the user to after authorization.""" + config: dict[str, str] | None = None + """Connect-time config values for the provider-declared `installation`-scope fields (e.g. a Zendesk `subdomain`), keyed by the config field. Only fields the provider declares may be supplied, and required fields must be provided unless already pinned on the integration.""" + + @classmethod + def from_dict( + cls, data: dict[str, Any] + ) -> AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequest: + """Deserialize from a dictionary.""" + try: + return cls( + user_id=data["user_id"], + connection_intent=data.get("connection_intent", "add"), + account_display_name=data.get("account_display_name"), + organization_id=data.get("organization_id"), + connection_owner=AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner( + _v_connection_owner + ) + if (_v_connection_owner := data.get("connection_owner")) is not None + else None, + return_to=data.get("return_to"), + config=data.get("config"), + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error( + "AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequest", e + ) + + def to_dict(self) -> dict[str, Any]: + """Serialize to a dictionary.""" + result: dict[str, Any] = {} + result["user_id"] = self.user_id + result["connection_intent"] = self.connection_intent + if self.account_display_name is not None: + result["account_display_name"] = self.account_display_name + else: + result["account_display_name"] = None + if self.organization_id is not None: + result["organization_id"] = self.organization_id + if self.connection_owner is not None: + result["connection_owner"] = ( + self.connection_owner.value + if isinstance(self.connection_owner, Enum) + else self.connection_owner + ) + if self.return_to is not None: + result["return_to"] = self.return_to + if self.config is not None: + result["config"] = self.config + return result diff --git a/src/workos/pipes/models/connected_account_input.py b/src/workos/pipes/models/connected_account_input.py new file mode 100644 index 00000000..7312b16f --- /dev/null +++ b/src/workos/pipes/models/connected_account_input.py @@ -0,0 +1,78 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from datetime import datetime +from enum import Enum +from typing import Any + +from workos._types import _format_datetime, _parse_datetime, _raise_deserialize_error +from workos.common.models.connected_account_input_state import ( + ConnectedAccountInputState, +) + + +@dataclass(slots=True) +class ConnectedAccountInput: + """Connected Account Input model.""" + + account_display_name: str | None = None + """A mutable, non-unique connection label; null clears it. Not an account identifier.""" + account_identifier: str | None = None + """A developer-attested account identifier; null clears it.""" + access_token: str | None = None + """The OAuth access token for the connected account.""" + refresh_token: str | None = None + """The OAuth refresh token for the connected account.""" + expires_at: datetime | None = None + """The ISO-8601 timestamp when the access token expires. Required when `access_token` is provided for tokens that expire.""" + scopes: list[str] | None = None + """The OAuth scopes granted for this connection.""" + state: ConnectedAccountInputState | None = None + """Explicitly set the state of the connected account. When omitted, the state is derived from the token combination provided.""" + + @classmethod + def from_dict(cls, data: dict[str, Any]) -> ConnectedAccountInput: + """Deserialize from a dictionary.""" + try: + return cls( + account_display_name=data.get("account_display_name"), + account_identifier=data.get("account_identifier"), + access_token=data.get("access_token"), + refresh_token=data.get("refresh_token"), + expires_at=_parse_datetime(_v_expires_at) + if (_v_expires_at := data.get("expires_at")) is not None + else None, + scopes=data.get("scopes"), + state=ConnectedAccountInputState(_v_state) + if (_v_state := data.get("state")) is not None + else None, + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error("ConnectedAccountInput", e) + + def to_dict(self) -> dict[str, Any]: + """Serialize to a dictionary.""" + result: dict[str, Any] = {} + if self.account_display_name is not None: + result["account_display_name"] = self.account_display_name + else: + result["account_display_name"] = None + if self.account_identifier is not None: + result["account_identifier"] = self.account_identifier + else: + result["account_identifier"] = None + if self.access_token is not None: + result["access_token"] = self.access_token + if self.refresh_token is not None: + result["refresh_token"] = self.refresh_token + if self.expires_at is not None: + result["expires_at"] = _format_datetime(self.expires_at) + if self.scopes is not None: + result["scopes"] = self.scopes + if self.state is not None: + result["state"] = ( + self.state.value if isinstance(self.state, Enum) else self.state + ) + return result diff --git a/src/workos/pipes/models/create_connected_account.py b/src/workos/pipes/models/create_connected_account.py new file mode 100644 index 00000000..40d997ac --- /dev/null +++ b/src/workos/pipes/models/create_connected_account.py @@ -0,0 +1,83 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from datetime import datetime +from enum import Enum +from typing import Any, Literal + +from workos._types import _format_datetime, _parse_datetime, _raise_deserialize_error +from workos.common.models.create_connected_account_state import ( + CreateConnectedAccountState, +) + + +@dataclass(slots=True) +class CreateConnectedAccount: + """Create Connected Account model.""" + + account_display_name: str | None = None + """A mutable, non-unique connection label; null clears it. Not an account identifier.""" + account_identifier: str | None = None + """A developer-attested account identifier; null clears it.""" + access_token: str | None = None + """The OAuth access token for the connected account.""" + refresh_token: str | None = None + """The OAuth refresh token for the connected account.""" + expires_at: datetime | None = None + """The ISO-8601 timestamp when the access token expires. Required when `access_token` is provided for tokens that expire.""" + scopes: list[str] | None = None + """The OAuth scopes granted for this connection.""" + state: CreateConnectedAccountState | None = None + """Explicitly set the state of the connected account. When omitted, the state is derived from the token combination provided.""" + connection_intent: Literal["add"] | None = None + """Set to `add` to create another connected account. Omit this field for permanent compatibility behavior. Creating an additional connection is not yet available: until it is, `add` succeeds only when the owner has no connection for this integration, which creates the compatibility connection, and otherwise returns 404 `multiple_connections_unavailable`.""" + + @classmethod + def from_dict(cls, data: dict[str, Any]) -> CreateConnectedAccount: + """Deserialize from a dictionary.""" + try: + return cls( + account_display_name=data.get("account_display_name"), + account_identifier=data.get("account_identifier"), + access_token=data.get("access_token"), + refresh_token=data.get("refresh_token"), + expires_at=_parse_datetime(_v_expires_at) + if (_v_expires_at := data.get("expires_at")) is not None + else None, + scopes=data.get("scopes"), + state=CreateConnectedAccountState(_v_state) + if (_v_state := data.get("state")) is not None + else None, + connection_intent=data.get("connection_intent"), + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error("CreateConnectedAccount", e) + + def to_dict(self) -> dict[str, Any]: + """Serialize to a dictionary.""" + result: dict[str, Any] = {} + if self.account_display_name is not None: + result["account_display_name"] = self.account_display_name + else: + result["account_display_name"] = None + if self.account_identifier is not None: + result["account_identifier"] = self.account_identifier + else: + result["account_identifier"] = None + if self.access_token is not None: + result["access_token"] = self.access_token + if self.refresh_token is not None: + result["refresh_token"] = self.refresh_token + if self.expires_at is not None: + result["expires_at"] = _format_datetime(self.expires_at) + if self.scopes is not None: + result["scopes"] = self.scopes + if self.state is not None: + result["state"] = ( + self.state.value if isinstance(self.state, Enum) else self.state + ) + if self.connection_intent is not None: + result["connection_intent"] = self.connection_intent + return result diff --git a/src/workos/pipes/models/create_organization_connected_account.py b/src/workos/pipes/models/create_organization_connected_account.py new file mode 100644 index 00000000..b5743439 --- /dev/null +++ b/src/workos/pipes/models/create_organization_connected_account.py @@ -0,0 +1,87 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from datetime import datetime +from enum import Enum +from typing import Any, Literal + +from workos._types import _format_datetime, _parse_datetime, _raise_deserialize_error +from workos.common.models.create_organization_connected_account_state import ( + CreateOrganizationConnectedAccountState, +) + + +@dataclass(slots=True) +class CreateOrganizationConnectedAccount: + """Create Organization Connected Account model.""" + + user_id: str + """The [User](https://workos.com/docs/reference/authkit/user) identifier of the organization member on whose behalf the connected account is being imported or updated. The user must be an active member of the organization.""" + account_display_name: str | None = None + """A mutable, non-unique connection label; null clears it. Not an account identifier.""" + account_identifier: str | None = None + """A developer-attested account identifier; null clears it.""" + access_token: str | None = None + """The OAuth access token for the connected account.""" + refresh_token: str | None = None + """The OAuth refresh token for the connected account.""" + expires_at: datetime | None = None + """The ISO-8601 timestamp when the access token expires. Required when `access_token` is provided for tokens that expire.""" + scopes: list[str] | None = None + """The OAuth scopes granted for this connection.""" + state: CreateOrganizationConnectedAccountState | None = None + """Explicitly set the state of the connected account. When omitted, the state is derived from the token combination provided.""" + connection_intent: Literal["add"] | None = None + """Set to `add` to create another connected account. Omit this field for permanent compatibility behavior. Creating an additional connection is not yet available: until it is, `add` succeeds only when the owner has no connection for this integration, which creates the compatibility connection, and otherwise returns 404 `multiple_connections_unavailable`.""" + + @classmethod + def from_dict(cls, data: dict[str, Any]) -> CreateOrganizationConnectedAccount: + """Deserialize from a dictionary.""" + try: + return cls( + user_id=data["user_id"], + account_display_name=data.get("account_display_name"), + account_identifier=data.get("account_identifier"), + access_token=data.get("access_token"), + refresh_token=data.get("refresh_token"), + expires_at=_parse_datetime(_v_expires_at) + if (_v_expires_at := data.get("expires_at")) is not None + else None, + scopes=data.get("scopes"), + state=CreateOrganizationConnectedAccountState(_v_state) + if (_v_state := data.get("state")) is not None + else None, + connection_intent=data.get("connection_intent"), + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error("CreateOrganizationConnectedAccount", e) + + def to_dict(self) -> dict[str, Any]: + """Serialize to a dictionary.""" + result: dict[str, Any] = {} + result["user_id"] = self.user_id + if self.account_display_name is not None: + result["account_display_name"] = self.account_display_name + else: + result["account_display_name"] = None + if self.account_identifier is not None: + result["account_identifier"] = self.account_identifier + else: + result["account_identifier"] = None + if self.access_token is not None: + result["access_token"] = self.access_token + if self.refresh_token is not None: + result["refresh_token"] = self.refresh_token + if self.expires_at is not None: + result["expires_at"] = _format_datetime(self.expires_at) + if self.scopes is not None: + result["scopes"] = self.scopes + if self.state is not None: + result["state"] = ( + self.state.value if isinstance(self.state, Enum) else self.state + ) + if self.connection_intent is not None: + result["connection_intent"] = self.connection_intent + return result diff --git a/src/workos/pipes/models/data_integration_credentials_response.py b/src/workos/pipes/models/data_integration_credentials_response.py index 54f33d1e..a4ad02af 100644 --- a/src/workos/pipes/models/data_integration_credentials_response.py +++ b/src/workos/pipes/models/data_integration_credentials_response.py @@ -11,9 +11,7 @@ DataIntegrationCredentialsResponseError, ) -from .data_integration_credentials_response_credential import ( - DataIntegrationCredentialsResponseCredential, -) +from .data_integration_vended_credential import DataIntegrationVendedCredential @dataclass(slots=True) @@ -22,7 +20,7 @@ class DataIntegrationCredentialsResponse: active: Literal[True] | None = None """Indicates credentials are available.""" - credential: DataIntegrationCredentialsResponseCredential | None = None + credential: DataIntegrationVendedCredential | None = None """The credential object containing the vended secret.""" error: DataIntegrationCredentialsResponseError | None = None """The reason credentials are unavailable. Additional values may be added in the future; handle unknown values gracefully. @@ -35,7 +33,7 @@ def from_dict(cls, data: dict[str, Any]) -> DataIntegrationCredentialsResponse: try: return cls( active=data.get("active"), - credential=DataIntegrationCredentialsResponseCredential.from_dict( + credential=DataIntegrationVendedCredential.from_dict( cast(dict[str, Any], _v_credential) ) if (_v_credential := data.get("credential")) is not None diff --git a/src/workos/pipes/models/data_integration_credentials_response_credential.py b/src/workos/pipes/models/data_integration_credentials_response_credential.py deleted file mode 100644 index 539d3b72..00000000 --- a/src/workos/pipes/models/data_integration_credentials_response_credential.py +++ /dev/null @@ -1,57 +0,0 @@ -# This file is auto-generated by oagen. Do not edit. - -from __future__ import annotations - -from dataclasses import dataclass -from typing import Any, Literal - -from workos._types import _raise_deserialize_error - - -@dataclass(slots=True) -class DataIntegrationCredentialsResponseCredential: - """The credential object containing the vended secret.""" - - object: Literal["credential"] - """Distinguishes the credential object.""" - auth_method: Literal["oauth"] - """The authentication method for this credential. Additional values may be added in the future; handle unknown values gracefully.""" - value: str - """The OAuth access token.""" - expires_at: str | None - """The ISO-8601 formatted timestamp indicating when the credential expires.""" - scopes: list[str] - """The scopes granted to the access token.""" - missing_scopes: list[str] - """If the integration has requested scopes that aren't present on the access token, they're listed here.""" - - @classmethod - def from_dict( - cls, data: dict[str, Any] - ) -> DataIntegrationCredentialsResponseCredential: - """Deserialize from a dictionary.""" - try: - return cls( - object=data.get("object", "credential"), - auth_method=data.get("auth_method", "oauth"), - value=data["value"], - expires_at=data["expires_at"], - scopes=data["scopes"], - missing_scopes=data["missing_scopes"], - ) - except (KeyError, ValueError) as e: - _raise_deserialize_error("DataIntegrationCredentialsResponseCredential", e) - - def to_dict(self) -> dict[str, Any]: - """Serialize to a dictionary.""" - result: dict[str, Any] = {} - result["object"] = self.object - result["auth_method"] = self.auth_method - result["value"] = self.value - if self.expires_at is not None: - result["expires_at"] = self.expires_at - else: - result["expires_at"] = None - result["scopes"] = self.scopes - result["missing_scopes"] = self.missing_scopes - return result diff --git a/src/workos/pipes/models/data_integration_vended_credential.py b/src/workos/pipes/models/data_integration_vended_credential.py new file mode 100644 index 00000000..0c93856b --- /dev/null +++ b/src/workos/pipes/models/data_integration_vended_credential.py @@ -0,0 +1,70 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, Literal + +from workos._types import _raise_deserialize_error + + +@dataclass(slots=True) +class DataIntegrationVendedCredential: + """Data Integration Vended Credential model.""" + + object: Literal["credential"] | None = None + """Distinguishes the credential object.""" + auth_method: Literal["oauth"] | None = None + """The authentication method for this credential. Additional values may be added in the future; handle unknown values gracefully.""" + value: str | None = None + """The vended secret. An OAuth access token when `auth_method` is `oauth`, the API key when `api_key`, or the client-credentials access token when `client_credentials`.""" + expires_at: str | None = None + """The ISO-8601 formatted timestamp indicating when the credential expires, or `null` if it does not expire. Present for `oauth` and `client_credentials` credentials; absent for `api_key`.""" + scopes: list[str] | None = None + """The scopes granted to the access token. Present for `oauth` and `client_credentials` credentials; absent for `api_key`.""" + missing_scopes: list[str] | None = None + """If the integration has requested scopes that aren't present on the access token, they're listed here. Present for `oauth` and `client_credentials` credentials; absent for `api_key`.""" + config: dict[str, str] | None = None + """Provider-declared, non-secret config from the installation snapshot, with current defaults for unset fields. Includes both integration- and installation-scope fields; omits undeclared fields and fields marked secret. Use these values to address a per-tenant host, such as Snowflake's `account` or Zendesk's `subdomain`. Empty when no values are disclosable. Changes to integration or organization pins require reconnecting or explicitly rebinding the connection. Defaults are live and can differ from a cached token's audience until refresh or re-mint.""" + metadata: dict[str, Any] | None = None + """Non-sensitive fields captured from the provider token response (e.g. Salesforce `instance_url`), as configured for the provider. Only present for `client_credentials` credentials.""" + + @classmethod + def from_dict(cls, data: dict[str, Any]) -> DataIntegrationVendedCredential: + """Deserialize from a dictionary.""" + try: + return cls( + object=data.get("object"), + auth_method=data.get("auth_method"), + value=data.get("value"), + expires_at=data.get("expires_at"), + scopes=data.get("scopes"), + missing_scopes=data.get("missing_scopes"), + config=data.get("config"), + metadata=data.get("metadata"), + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error("DataIntegrationVendedCredential", e) + + def to_dict(self) -> dict[str, Any]: + """Serialize to a dictionary.""" + result: dict[str, Any] = {} + if self.object is not None: + result["object"] = self.object + if self.auth_method is not None: + result["auth_method"] = self.auth_method + if self.value is not None: + result["value"] = self.value + if self.expires_at is not None: + result["expires_at"] = self.expires_at + else: + result["expires_at"] = None + if self.scopes is not None: + result["scopes"] = self.scopes + if self.missing_scopes is not None: + result["missing_scopes"] = self.missing_scopes + if self.config is not None: + result["config"] = self.config + if self.metadata is not None: + result["metadata"] = self.metadata + return result diff --git a/src/workos/pipes/models/data_integrations_create_api_key_connection_request.py b/src/workos/pipes/models/data_integrations_create_api_key_connection_request.py new file mode 100644 index 00000000..a2d92deb --- /dev/null +++ b/src/workos/pipes/models/data_integrations_create_api_key_connection_request.py @@ -0,0 +1,80 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from enum import Enum +from typing import Any, Literal + +from workos._types import _raise_deserialize_error +from workos.common.models.data_integrations_create_api_key_connection_request_connection_owner import ( + DataIntegrationsCreateApiKeyConnectionRequestConnectionOwner, +) + + +@dataclass(slots=True) +class DataIntegrationsCreateApiKeyConnectionRequest: + """Data Integrations Create Api Key Connection Request model.""" + + user_id: str + """A [User](https://workos.com/docs/reference/authkit/user) identifier.""" + secret: str + """The API key secret to store for this integration.""" + connection_intent: Literal["add"] + """Must be `add`: this endpoint only creates another connection. The first connection for an owner shape fills the compatibility slot; later connections are standard. Creating an additional connection is not yet available: until it is, `add` succeeds only when the owner has no connection for this integration and otherwise returns 404 `multiple_connections_unavailable`.""" + account_display_name: str | None = None + """A mutable, non-unique label. Omit to preserve an existing label; null clears it. Labels are never account identifiers.""" + account_identifier: str | None = None + """A developer-attested provider account identifier, never inferred from a label or credential. Omit to preserve an existing identifier; null clears it.""" + organization_id: str | None = None + """An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to scope the connection to a specific organization. Required when `connection_owner` is `organization`.""" + connection_owner: ( + DataIntegrationsCreateApiKeyConnectionRequestConnectionOwner | None + ) = None + """Whose connection to create or rotate. `user` (the default) addresses the connection owned by `user_id`. `organization` addresses the connection shared by every member of `organization_id`; `user_id` then identifies the member performing the request and must be an active member of the organization.""" + + @classmethod + def from_dict( + cls, data: dict[str, Any] + ) -> DataIntegrationsCreateApiKeyConnectionRequest: + """Deserialize from a dictionary.""" + try: + return cls( + user_id=data["user_id"], + secret=data["secret"], + connection_intent=data.get("connection_intent", "add"), + account_display_name=data.get("account_display_name"), + account_identifier=data.get("account_identifier"), + organization_id=data.get("organization_id"), + connection_owner=DataIntegrationsCreateApiKeyConnectionRequestConnectionOwner( + _v_connection_owner + ) + if (_v_connection_owner := data.get("connection_owner")) is not None + else None, + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error("DataIntegrationsCreateApiKeyConnectionRequest", e) + + def to_dict(self) -> dict[str, Any]: + """Serialize to a dictionary.""" + result: dict[str, Any] = {} + result["user_id"] = self.user_id + result["secret"] = self.secret + result["connection_intent"] = self.connection_intent + if self.account_display_name is not None: + result["account_display_name"] = self.account_display_name + else: + result["account_display_name"] = None + if self.account_identifier is not None: + result["account_identifier"] = self.account_identifier + else: + result["account_identifier"] = None + if self.organization_id is not None: + result["organization_id"] = self.organization_id + if self.connection_owner is not None: + result["connection_owner"] = ( + self.connection_owner.value + if isinstance(self.connection_owner, Enum) + else self.connection_owner + ) + return result diff --git a/src/workos/pipes/models/data_integrations_create_client_credentials_connection_request.py b/src/workos/pipes/models/data_integrations_create_client_credentials_connection_request.py new file mode 100644 index 00000000..4ed7795a --- /dev/null +++ b/src/workos/pipes/models/data_integrations_create_client_credentials_connection_request.py @@ -0,0 +1,91 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from enum import Enum +from typing import Any, Literal + +from workos._types import _raise_deserialize_error +from workos.common.models.data_integrations_create_client_credentials_connection_request_connection_owner import ( + DataIntegrationsCreateClientCredentialsConnectionRequestConnectionOwner, +) + + +@dataclass(slots=True) +class DataIntegrationsCreateClientCredentialsConnectionRequest: + """Data Integrations Create Client Credentials Connection Request model.""" + + user_id: str + """A [User](https://workos.com/docs/reference/authkit/user) identifier.""" + client_id: str + """The OAuth client ID to store for this integration.""" + client_secret: str + """The OAuth client secret to store for this integration.""" + connection_intent: Literal["add"] + """Must be `add`: this endpoint only creates another connection. The first connection for an owner shape fills the compatibility slot; later connections are standard. Creating an additional connection is not yet available: until it is, `add` succeeds only when the owner has no connection for this integration and otherwise returns 404 `multiple_connections_unavailable`.""" + account_display_name: str | None = None + """A mutable, non-unique label. Omit to preserve an existing label; null clears it. Labels are never account identifiers.""" + account_identifier: str | None = None + """A developer-attested provider account identifier, never inferred from a label or credential. Omit to preserve an existing identifier; null clears it.""" + organization_id: str | None = None + """An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to scope the connection to a specific organization. Required when `connection_owner` is `organization`.""" + connection_owner: ( + DataIntegrationsCreateClientCredentialsConnectionRequestConnectionOwner | None + ) = None + """Whose connection to create or rotate. `user` (the default) addresses the connection owned by `user_id`. `organization` addresses the connection shared by every member of `organization_id`; `user_id` then identifies the member performing the request and must be an active member of the organization.""" + config: dict[str, str] | None = None + """Provider-specific configuration values collected for this installation, keyed by the provider's config field descriptors.""" + + @classmethod + def from_dict( + cls, data: dict[str, Any] + ) -> DataIntegrationsCreateClientCredentialsConnectionRequest: + """Deserialize from a dictionary.""" + try: + return cls( + user_id=data["user_id"], + client_id=data["client_id"], + client_secret=data["client_secret"], + connection_intent=data.get("connection_intent", "add"), + account_display_name=data.get("account_display_name"), + account_identifier=data.get("account_identifier"), + organization_id=data.get("organization_id"), + connection_owner=DataIntegrationsCreateClientCredentialsConnectionRequestConnectionOwner( + _v_connection_owner + ) + if (_v_connection_owner := data.get("connection_owner")) is not None + else None, + config=data.get("config"), + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error( + "DataIntegrationsCreateClientCredentialsConnectionRequest", e + ) + + def to_dict(self) -> dict[str, Any]: + """Serialize to a dictionary.""" + result: dict[str, Any] = {} + result["user_id"] = self.user_id + result["client_id"] = self.client_id + result["client_secret"] = self.client_secret + result["connection_intent"] = self.connection_intent + if self.account_display_name is not None: + result["account_display_name"] = self.account_display_name + else: + result["account_display_name"] = None + if self.account_identifier is not None: + result["account_identifier"] = self.account_identifier + else: + result["account_identifier"] = None + if self.organization_id is not None: + result["organization_id"] = self.organization_id + if self.connection_owner is not None: + result["connection_owner"] = ( + self.connection_owner.value + if isinstance(self.connection_owner, Enum) + else self.connection_owner + ) + if self.config is not None: + result["config"] = self.config + return result diff --git a/src/workos/pipes/models/data_integrations_get_data_integration_authorize_url_request.py b/src/workos/pipes/models/data_integrations_get_data_integration_authorize_url_request.py index 1ca8b5d5..929f2b03 100644 --- a/src/workos/pipes/models/data_integrations_get_data_integration_authorize_url_request.py +++ b/src/workos/pipes/models/data_integrations_get_data_integration_authorize_url_request.py @@ -18,6 +18,8 @@ class DataIntegrationsGetDataIntegrationAuthorizeUrlRequest: user_id: str """The ID of the user to authorize. When `connection_owner` is `organization`, this is the user authorizing on behalf of the organization; they must be an active member of the organization and do not become the owner of the resulting connected account.""" + account_display_name: str | None = None + """A mutable, non-unique label. Omit to preserve an existing label; null clears it. Labels are never account identifiers.""" organization_id: str | None = None """An organization ID to scope the authorization to a specific organization. Required when `connection_owner` is `organization`.""" connection_owner: ( @@ -37,6 +39,7 @@ def from_dict( try: return cls( user_id=data["user_id"], + account_display_name=data.get("account_display_name"), organization_id=data.get("organization_id"), connection_owner=DataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner( _v_connection_owner @@ -55,6 +58,10 @@ def to_dict(self) -> dict[str, Any]: """Serialize to a dictionary.""" result: dict[str, Any] = {} result["user_id"] = self.user_id + if self.account_display_name is not None: + result["account_display_name"] = self.account_display_name + else: + result["account_display_name"] = None if self.organization_id is not None: result["organization_id"] = self.organization_id if self.connection_owner is not None: diff --git a/src/workos/pipes/models/data_integrations_upsert_api_key_request.py b/src/workos/pipes/models/data_integrations_upsert_api_key_request.py index 199f6cf8..88b98a8a 100644 --- a/src/workos/pipes/models/data_integrations_upsert_api_key_request.py +++ b/src/workos/pipes/models/data_integrations_upsert_api_key_request.py @@ -20,10 +20,12 @@ class DataIntegrationsUpsertApiKeyRequest: """A [User](https://workos.com/docs/reference/authkit/user) identifier.""" secret: str """The API key secret to store for this integration.""" + account_display_name: str | None = None + """A mutable, non-unique label. Omit to preserve an existing label; null clears it. Labels are never account identifiers.""" + account_identifier: str | None = None + """A developer-attested provider account identifier, never inferred from a label or credential. Omit to preserve an existing identifier; null clears it.""" organization_id: str | None = None """An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to scope the connection to a specific organization. Required when `connection_owner` is `organization`.""" - connected_account_id: str | None = None - """A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to rotate a specific existing connection.""" connection_owner: DataIntegrationsUpsertApiKeyRequestConnectionOwner | None = None """Whose connection to create or rotate. `user` (the default) addresses the connection owned by `user_id`. `organization` addresses the connection shared by every member of `organization_id`; `user_id` then identifies the member performing the request and must be an active member of the organization.""" @@ -34,8 +36,9 @@ def from_dict(cls, data: dict[str, Any]) -> DataIntegrationsUpsertApiKeyRequest: return cls( user_id=data["user_id"], secret=data["secret"], + account_display_name=data.get("account_display_name"), + account_identifier=data.get("account_identifier"), organization_id=data.get("organization_id"), - connected_account_id=data.get("connected_account_id"), connection_owner=DataIntegrationsUpsertApiKeyRequestConnectionOwner( _v_connection_owner ) @@ -50,10 +53,16 @@ def to_dict(self) -> dict[str, Any]: result: dict[str, Any] = {} result["user_id"] = self.user_id result["secret"] = self.secret + if self.account_display_name is not None: + result["account_display_name"] = self.account_display_name + else: + result["account_display_name"] = None + if self.account_identifier is not None: + result["account_identifier"] = self.account_identifier + else: + result["account_identifier"] = None if self.organization_id is not None: result["organization_id"] = self.organization_id - if self.connected_account_id is not None: - result["connected_account_id"] = self.connected_account_id if self.connection_owner is not None: result["connection_owner"] = ( self.connection_owner.value diff --git a/src/workos/pipes/models/data_integrations_upsert_api_key_request_2.py b/src/workos/pipes/models/data_integrations_upsert_api_key_request_2.py new file mode 100644 index 00000000..951fa849 --- /dev/null +++ b/src/workos/pipes/models/data_integrations_upsert_api_key_request_2.py @@ -0,0 +1,76 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from enum import Enum +from typing import Any + +from workos._types import _raise_deserialize_error +from workos.common.models.data_integrations_upsert_api_key_request_2_connection_owner import ( + DataIntegrationsUpsertApiKeyRequest2ConnectionOwner, +) + + +@dataclass(slots=True) +class DataIntegrationsUpsertApiKeyRequest2: + """Data Integrations Upsert Api Key Request2 model.""" + + user_id: str + """A [User](https://workos.com/docs/reference/authkit/user) identifier.""" + secret: str + """The API key secret to store for this integration.""" + connected_account_id: str + """The exact connected account to reauthorize. The reauthorize intent may be omitted for compatibility.""" + account_display_name: str | None = None + """A mutable, non-unique label. Omit to preserve an existing label; null clears it. Labels are never account identifiers.""" + account_identifier: str | None = None + """A developer-attested provider account identifier, never inferred from a label or credential. Omit to preserve an existing identifier; null clears it.""" + organization_id: str | None = None + """An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to scope the connection to a specific organization. Required when `connection_owner` is `organization`.""" + connection_owner: DataIntegrationsUpsertApiKeyRequest2ConnectionOwner | None = None + """Whose connection to create or rotate. `user` (the default) addresses the connection owned by `user_id`. `organization` addresses the connection shared by every member of `organization_id`; `user_id` then identifies the member performing the request and must be an active member of the organization.""" + + @classmethod + def from_dict(cls, data: dict[str, Any]) -> DataIntegrationsUpsertApiKeyRequest2: + """Deserialize from a dictionary.""" + try: + return cls( + user_id=data["user_id"], + secret=data["secret"], + connected_account_id=data["connected_account_id"], + account_display_name=data.get("account_display_name"), + account_identifier=data.get("account_identifier"), + organization_id=data.get("organization_id"), + connection_owner=DataIntegrationsUpsertApiKeyRequest2ConnectionOwner( + _v_connection_owner + ) + if (_v_connection_owner := data.get("connection_owner")) is not None + else None, + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error("DataIntegrationsUpsertApiKeyRequest2", e) + + def to_dict(self) -> dict[str, Any]: + """Serialize to a dictionary.""" + result: dict[str, Any] = {} + result["user_id"] = self.user_id + result["secret"] = self.secret + result["connected_account_id"] = self.connected_account_id + if self.account_display_name is not None: + result["account_display_name"] = self.account_display_name + else: + result["account_display_name"] = None + if self.account_identifier is not None: + result["account_identifier"] = self.account_identifier + else: + result["account_identifier"] = None + if self.organization_id is not None: + result["organization_id"] = self.organization_id + if self.connection_owner is not None: + result["connection_owner"] = ( + self.connection_owner.value + if isinstance(self.connection_owner, Enum) + else self.connection_owner + ) + return result diff --git a/src/workos/pipes/models/data_integrations_upsert_client_credentials_request.py b/src/workos/pipes/models/data_integrations_upsert_client_credentials_request.py index 56efd7ba..55e989df 100644 --- a/src/workos/pipes/models/data_integrations_upsert_client_credentials_request.py +++ b/src/workos/pipes/models/data_integrations_upsert_client_credentials_request.py @@ -22,10 +22,12 @@ class DataIntegrationsUpsertClientCredentialsRequest: """The OAuth client ID to store for this integration.""" client_secret: str """The OAuth client secret to store for this integration.""" + account_display_name: str | None = None + """A mutable, non-unique label. Omit to preserve an existing label; null clears it. Labels are never account identifiers.""" + account_identifier: str | None = None + """A developer-attested provider account identifier, never inferred from a label or credential. Omit to preserve an existing identifier; null clears it.""" organization_id: str | None = None """An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to scope the connection to a specific organization. Required when `connection_owner` is `organization`.""" - connected_account_id: str | None = None - """A [connected account](https://workos.com/docs/reference/pipes/connected-account) identifier. Use this to rotate a specific existing connection.""" connection_owner: ( DataIntegrationsUpsertClientCredentialsRequestConnectionOwner | None ) = None @@ -43,8 +45,9 @@ def from_dict( user_id=data["user_id"], client_id=data["client_id"], client_secret=data["client_secret"], + account_display_name=data.get("account_display_name"), + account_identifier=data.get("account_identifier"), organization_id=data.get("organization_id"), - connected_account_id=data.get("connected_account_id"), connection_owner=DataIntegrationsUpsertClientCredentialsRequestConnectionOwner( _v_connection_owner ) @@ -63,10 +66,16 @@ def to_dict(self) -> dict[str, Any]: result["user_id"] = self.user_id result["client_id"] = self.client_id result["client_secret"] = self.client_secret + if self.account_display_name is not None: + result["account_display_name"] = self.account_display_name + else: + result["account_display_name"] = None + if self.account_identifier is not None: + result["account_identifier"] = self.account_identifier + else: + result["account_identifier"] = None if self.organization_id is not None: result["organization_id"] = self.organization_id - if self.connected_account_id is not None: - result["connected_account_id"] = self.connected_account_id if self.connection_owner is not None: result["connection_owner"] = ( self.connection_owner.value diff --git a/src/workos/pipes/models/data_integrations_upsert_client_credentials_request_2.py b/src/workos/pipes/models/data_integrations_upsert_client_credentials_request_2.py new file mode 100644 index 00000000..360bba99 --- /dev/null +++ b/src/workos/pipes/models/data_integrations_upsert_client_credentials_request_2.py @@ -0,0 +1,91 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from enum import Enum +from typing import Any + +from workos._types import _raise_deserialize_error +from workos.common.models.data_integrations_upsert_client_credentials_request_2_connection_owner import ( + DataIntegrationsUpsertClientCredentialsRequest2ConnectionOwner, +) + + +@dataclass(slots=True) +class DataIntegrationsUpsertClientCredentialsRequest2: + """Data Integrations Upsert Client Credentials Request2 model.""" + + user_id: str + """A [User](https://workos.com/docs/reference/authkit/user) identifier.""" + client_id: str + """The OAuth client ID to store for this integration.""" + client_secret: str + """The OAuth client secret to store for this integration.""" + connected_account_id: str + """The exact connected account to reauthorize. The reauthorize intent may be omitted for compatibility.""" + account_display_name: str | None = None + """A mutable, non-unique label. Omit to preserve an existing label; null clears it. Labels are never account identifiers.""" + account_identifier: str | None = None + """A developer-attested provider account identifier, never inferred from a label or credential. Omit to preserve an existing identifier; null clears it.""" + organization_id: str | None = None + """An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to scope the connection to a specific organization. Required when `connection_owner` is `organization`.""" + connection_owner: ( + DataIntegrationsUpsertClientCredentialsRequest2ConnectionOwner | None + ) = None + """Whose connection to create or rotate. `user` (the default) addresses the connection owned by `user_id`. `organization` addresses the connection shared by every member of `organization_id`; `user_id` then identifies the member performing the request and must be an active member of the organization.""" + config: dict[str, str] | None = None + """Provider-specific configuration values collected for this installation, keyed by the provider's config field descriptors.""" + + @classmethod + def from_dict( + cls, data: dict[str, Any] + ) -> DataIntegrationsUpsertClientCredentialsRequest2: + """Deserialize from a dictionary.""" + try: + return cls( + user_id=data["user_id"], + client_id=data["client_id"], + client_secret=data["client_secret"], + connected_account_id=data["connected_account_id"], + account_display_name=data.get("account_display_name"), + account_identifier=data.get("account_identifier"), + organization_id=data.get("organization_id"), + connection_owner=DataIntegrationsUpsertClientCredentialsRequest2ConnectionOwner( + _v_connection_owner + ) + if (_v_connection_owner := data.get("connection_owner")) is not None + else None, + config=data.get("config"), + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error( + "DataIntegrationsUpsertClientCredentialsRequest2", e + ) + + def to_dict(self) -> dict[str, Any]: + """Serialize to a dictionary.""" + result: dict[str, Any] = {} + result["user_id"] = self.user_id + result["client_id"] = self.client_id + result["client_secret"] = self.client_secret + result["connected_account_id"] = self.connected_account_id + if self.account_display_name is not None: + result["account_display_name"] = self.account_display_name + else: + result["account_display_name"] = None + if self.account_identifier is not None: + result["account_identifier"] = self.account_identifier + else: + result["account_identifier"] = None + if self.organization_id is not None: + result["organization_id"] = self.organization_id + if self.connection_owner is not None: + result["connection_owner"] = ( + self.connection_owner.value + if isinstance(self.connection_owner, Enum) + else self.connection_owner + ) + if self.config is not None: + result["config"] = self.config + return result diff --git a/src/workos/pipes/models/organization_connected_account.py b/src/workos/pipes/models/organization_connected_account.py new file mode 100644 index 00000000..2adce19d --- /dev/null +++ b/src/workos/pipes/models/organization_connected_account.py @@ -0,0 +1,82 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from datetime import datetime +from enum import Enum +from typing import Any + +from workos._types import _format_datetime, _parse_datetime, _raise_deserialize_error +from workos.common.models.organization_connected_account_state import ( + OrganizationConnectedAccountState, +) + + +@dataclass(slots=True) +class OrganizationConnectedAccount: + """Organization Connected Account model.""" + + user_id: str + """The [User](https://workos.com/docs/reference/authkit/user) identifier of the organization member on whose behalf the connected account is being imported or updated. The user must be an active member of the organization.""" + account_display_name: str | None = None + """A mutable, non-unique connection label; null clears it. Not an account identifier.""" + account_identifier: str | None = None + """A developer-attested account identifier; null clears it.""" + access_token: str | None = None + """The OAuth access token for the connected account.""" + refresh_token: str | None = None + """The OAuth refresh token for the connected account.""" + expires_at: datetime | None = None + """The ISO-8601 timestamp when the access token expires. Required when `access_token` is provided for tokens that expire.""" + scopes: list[str] | None = None + """The OAuth scopes granted for this connection.""" + state: OrganizationConnectedAccountState | None = None + """Explicitly set the state of the connected account. When omitted, the state is derived from the token combination provided.""" + + @classmethod + def from_dict(cls, data: dict[str, Any]) -> OrganizationConnectedAccount: + """Deserialize from a dictionary.""" + try: + return cls( + user_id=data["user_id"], + account_display_name=data.get("account_display_name"), + account_identifier=data.get("account_identifier"), + access_token=data.get("access_token"), + refresh_token=data.get("refresh_token"), + expires_at=_parse_datetime(_v_expires_at) + if (_v_expires_at := data.get("expires_at")) is not None + else None, + scopes=data.get("scopes"), + state=OrganizationConnectedAccountState(_v_state) + if (_v_state := data.get("state")) is not None + else None, + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error("OrganizationConnectedAccount", e) + + def to_dict(self) -> dict[str, Any]: + """Serialize to a dictionary.""" + result: dict[str, Any] = {} + result["user_id"] = self.user_id + if self.account_display_name is not None: + result["account_display_name"] = self.account_display_name + else: + result["account_display_name"] = None + if self.account_identifier is not None: + result["account_identifier"] = self.account_identifier + else: + result["account_identifier"] = None + if self.access_token is not None: + result["access_token"] = self.access_token + if self.refresh_token is not None: + result["refresh_token"] = self.refresh_token + if self.expires_at is not None: + result["expires_at"] = _format_datetime(self.expires_at) + if self.scopes is not None: + result["scopes"] = self.scopes + if self.state is not None: + result["state"] = ( + self.state.value if isinstance(self.state, Enum) else self.state + ) + return result diff --git a/src/workos/pipes/models/pipes_ownership.py b/src/workos/pipes/models/pipes_ownership.py index 2abef504..43910c3f 100644 --- a/src/workos/pipes/models/pipes_ownership.py +++ b/src/workos/pipes/models/pipes_ownership.py @@ -3,18 +3,20 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: - from workos.common.models.create_data_integration_ownership import ( - CreateDataIntegrationOwnership as PipesOwnership, + from workos.common.models.add_data_integrations_get_data_integration_authorize_url_request_connection_owner import ( + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner as PipesOwnership, ) else: def __getattr__(name: str): if name == "PipesOwnership": - from workos.common.models.create_data_integration_ownership import ( - CreateDataIntegrationOwnership, + from workos.common.models.add_data_integrations_get_data_integration_authorize_url_request_connection_owner import ( + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner, ) - return CreateDataIntegrationOwnership + return ( + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner + ) raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/src/workos/pipes/models/reauthorize_data_integrations_get_data_integration_authorize_url_request.py b/src/workos/pipes/models/reauthorize_data_integrations_get_data_integration_authorize_url_request.py new file mode 100644 index 00000000..687da40c --- /dev/null +++ b/src/workos/pipes/models/reauthorize_data_integrations_get_data_integration_authorize_url_request.py @@ -0,0 +1,86 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from enum import Enum +from typing import Any, Literal + +from workos._types import _raise_deserialize_error +from workos.common.models.reauthorize_data_integrations_get_data_integration_authorize_url_request_connection_owner import ( + ReauthorizeDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner, +) + + +@dataclass(slots=True) +class ReauthorizeDataIntegrationsGetDataIntegrationAuthorizeUrlRequest: + """Reauthorize Data Integrations Get Data Integration Authorize Url Request model.""" + + user_id: str + """The ID of the user to authorize. When `connection_owner` is `organization`, this is the user authorizing on behalf of the organization; they must be an active member of the organization and do not become the owner of the resulting connected account.""" + connection_intent: Literal["reauthorize"] + """Reauthorize exactly the connection named by `connected_account_id`.""" + connected_account_id: str + """The exact connected account to reauthorize. Required with `connection_intent: reauthorize`.""" + account_display_name: str | None = None + """A mutable, non-unique label. Omit to preserve an existing label; null clears it. Labels are never account identifiers.""" + organization_id: str | None = None + """An organization ID to scope the authorization to a specific organization. Required when `connection_owner` is `organization`.""" + connection_owner: ( + ReauthorizeDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner + | None + ) = None + """Who will own the connected account. `user` (the default) connects the user's own account. `organization` connects the organization's shared account and requires `organization_id`.""" + return_to: str | None = None + """The URL to redirect the user to after authorization.""" + config: dict[str, str] | None = None + """Connect-time config values for the provider-declared `installation`-scope fields (e.g. a Zendesk `subdomain`), keyed by the config field. Only fields the provider declares may be supplied, and required fields must be provided unless already pinned on the integration.""" + + @classmethod + def from_dict( + cls, data: dict[str, Any] + ) -> ReauthorizeDataIntegrationsGetDataIntegrationAuthorizeUrlRequest: + """Deserialize from a dictionary.""" + try: + return cls( + user_id=data["user_id"], + connection_intent=data.get("connection_intent", "reauthorize"), + connected_account_id=data["connected_account_id"], + account_display_name=data.get("account_display_name"), + organization_id=data.get("organization_id"), + connection_owner=ReauthorizeDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner( + _v_connection_owner + ) + if (_v_connection_owner := data.get("connection_owner")) is not None + else None, + return_to=data.get("return_to"), + config=data.get("config"), + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error( + "ReauthorizeDataIntegrationsGetDataIntegrationAuthorizeUrlRequest", e + ) + + def to_dict(self) -> dict[str, Any]: + """Serialize to a dictionary.""" + result: dict[str, Any] = {} + result["user_id"] = self.user_id + result["connection_intent"] = self.connection_intent + result["connected_account_id"] = self.connected_account_id + if self.account_display_name is not None: + result["account_display_name"] = self.account_display_name + else: + result["account_display_name"] = None + if self.organization_id is not None: + result["organization_id"] = self.organization_id + if self.connection_owner is not None: + result["connection_owner"] = ( + self.connection_owner.value + if isinstance(self.connection_owner, Enum) + else self.connection_owner + ) + if self.return_to is not None: + result["return_to"] = self.return_to + if self.config is not None: + result["config"] = self.config + return result diff --git a/src/workos/pipes/models/reauthorize_data_integrations_upsert_api_key_request.py b/src/workos/pipes/models/reauthorize_data_integrations_upsert_api_key_request.py new file mode 100644 index 00000000..3dc07bd3 --- /dev/null +++ b/src/workos/pipes/models/reauthorize_data_integrations_upsert_api_key_request.py @@ -0,0 +1,86 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from enum import Enum +from typing import Any, Literal + +from workos._types import _raise_deserialize_error +from workos.common.models.reauthorize_data_integrations_upsert_api_key_request_connection_owner import ( + ReauthorizeDataIntegrationsUpsertApiKeyRequestConnectionOwner, +) + + +@dataclass(slots=True) +class ReauthorizeDataIntegrationsUpsertApiKeyRequest: + """Reauthorize Data Integrations Upsert Api Key Request model.""" + + user_id: str + """A [User](https://workos.com/docs/reference/authkit/user) identifier.""" + secret: str + """The API key secret to store for this integration.""" + connection_intent: Literal["reauthorize"] + """Reauthorize exactly the connection named by `connected_account_id`.""" + connected_account_id: str + """The exact connected account to reauthorize. Required with `connection_intent: reauthorize`.""" + account_display_name: str | None = None + """A mutable, non-unique label. Omit to preserve an existing label; null clears it. Labels are never account identifiers.""" + account_identifier: str | None = None + """A developer-attested provider account identifier, never inferred from a label or credential. Omit to preserve an existing identifier; null clears it.""" + organization_id: str | None = None + """An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to scope the connection to a specific organization. Required when `connection_owner` is `organization`.""" + connection_owner: ( + ReauthorizeDataIntegrationsUpsertApiKeyRequestConnectionOwner | None + ) = None + """Whose connection to create or rotate. `user` (the default) addresses the connection owned by `user_id`. `organization` addresses the connection shared by every member of `organization_id`; `user_id` then identifies the member performing the request and must be an active member of the organization.""" + + @classmethod + def from_dict( + cls, data: dict[str, Any] + ) -> ReauthorizeDataIntegrationsUpsertApiKeyRequest: + """Deserialize from a dictionary.""" + try: + return cls( + user_id=data["user_id"], + secret=data["secret"], + connection_intent=data.get("connection_intent", "reauthorize"), + connected_account_id=data["connected_account_id"], + account_display_name=data.get("account_display_name"), + account_identifier=data.get("account_identifier"), + organization_id=data.get("organization_id"), + connection_owner=ReauthorizeDataIntegrationsUpsertApiKeyRequestConnectionOwner( + _v_connection_owner + ) + if (_v_connection_owner := data.get("connection_owner")) is not None + else None, + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error( + "ReauthorizeDataIntegrationsUpsertApiKeyRequest", e + ) + + def to_dict(self) -> dict[str, Any]: + """Serialize to a dictionary.""" + result: dict[str, Any] = {} + result["user_id"] = self.user_id + result["secret"] = self.secret + result["connection_intent"] = self.connection_intent + result["connected_account_id"] = self.connected_account_id + if self.account_display_name is not None: + result["account_display_name"] = self.account_display_name + else: + result["account_display_name"] = None + if self.account_identifier is not None: + result["account_identifier"] = self.account_identifier + else: + result["account_identifier"] = None + if self.organization_id is not None: + result["organization_id"] = self.organization_id + if self.connection_owner is not None: + result["connection_owner"] = ( + self.connection_owner.value + if isinstance(self.connection_owner, Enum) + else self.connection_owner + ) + return result diff --git a/src/workos/pipes/models/reauthorize_data_integrations_upsert_client_credentials_request.py b/src/workos/pipes/models/reauthorize_data_integrations_upsert_client_credentials_request.py new file mode 100644 index 00000000..9772770c --- /dev/null +++ b/src/workos/pipes/models/reauthorize_data_integrations_upsert_client_credentials_request.py @@ -0,0 +1,95 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from enum import Enum +from typing import Any, Literal + +from workos._types import _raise_deserialize_error +from workos.common.models.reauthorize_data_integrations_upsert_client_credentials_request_connection_owner import ( + ReauthorizeDataIntegrationsUpsertClientCredentialsRequestConnectionOwner, +) + + +@dataclass(slots=True) +class ReauthorizeDataIntegrationsUpsertClientCredentialsRequest: + """Reauthorize Data Integrations Upsert Client Credentials Request model.""" + + user_id: str + """A [User](https://workos.com/docs/reference/authkit/user) identifier.""" + client_id: str + """The OAuth client ID to store for this integration.""" + client_secret: str + """The OAuth client secret to store for this integration.""" + connection_intent: Literal["reauthorize"] + """Reauthorize exactly the connection named by `connected_account_id`.""" + connected_account_id: str + """The exact connected account to reauthorize. Required with `connection_intent: reauthorize`.""" + account_display_name: str | None = None + """A mutable, non-unique label. Omit to preserve an existing label; null clears it. Labels are never account identifiers.""" + account_identifier: str | None = None + """A developer-attested provider account identifier, never inferred from a label or credential. Omit to preserve an existing identifier; null clears it.""" + organization_id: str | None = None + """An [Organization](https://workos.com/docs/reference/organization) identifier. Optional parameter to scope the connection to a specific organization. Required when `connection_owner` is `organization`.""" + connection_owner: ( + ReauthorizeDataIntegrationsUpsertClientCredentialsRequestConnectionOwner | None + ) = None + """Whose connection to create or rotate. `user` (the default) addresses the connection owned by `user_id`. `organization` addresses the connection shared by every member of `organization_id`; `user_id` then identifies the member performing the request and must be an active member of the organization.""" + config: dict[str, str] | None = None + """Provider-specific configuration values collected for this installation, keyed by the provider's config field descriptors.""" + + @classmethod + def from_dict( + cls, data: dict[str, Any] + ) -> ReauthorizeDataIntegrationsUpsertClientCredentialsRequest: + """Deserialize from a dictionary.""" + try: + return cls( + user_id=data["user_id"], + client_id=data["client_id"], + client_secret=data["client_secret"], + connection_intent=data.get("connection_intent", "reauthorize"), + connected_account_id=data["connected_account_id"], + account_display_name=data.get("account_display_name"), + account_identifier=data.get("account_identifier"), + organization_id=data.get("organization_id"), + connection_owner=ReauthorizeDataIntegrationsUpsertClientCredentialsRequestConnectionOwner( + _v_connection_owner + ) + if (_v_connection_owner := data.get("connection_owner")) is not None + else None, + config=data.get("config"), + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error( + "ReauthorizeDataIntegrationsUpsertClientCredentialsRequest", e + ) + + def to_dict(self) -> dict[str, Any]: + """Serialize to a dictionary.""" + result: dict[str, Any] = {} + result["user_id"] = self.user_id + result["client_id"] = self.client_id + result["client_secret"] = self.client_secret + result["connection_intent"] = self.connection_intent + result["connected_account_id"] = self.connected_account_id + if self.account_display_name is not None: + result["account_display_name"] = self.account_display_name + else: + result["account_display_name"] = None + if self.account_identifier is not None: + result["account_identifier"] = self.account_identifier + else: + result["account_identifier"] = None + if self.organization_id is not None: + result["organization_id"] = self.organization_id + if self.connection_owner is not None: + result["connection_owner"] = ( + self.connection_owner.value + if isinstance(self.connection_owner, Enum) + else self.connection_owner + ) + if self.config is not None: + result["config"] = self.config + return result diff --git a/tests/fixtures/connected_account.json b/tests/fixtures/connected_account.json index c98e4187..f4764844 100644 --- a/tests/fixtures/connected_account.json +++ b/tests/fixtures/connected_account.json @@ -18,6 +18,8 @@ "instance_url": "https://example.my.salesforce.com" }, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": null, "created_at": "2024-01-16T14:20:00.000Z", "updated_at": "2024-01-16T14:20:00.000Z" } diff --git a/tests/fixtures/connected_account_input.json b/tests/fixtures/connected_account_input.json index d04b7e83..0a181441 100644 --- a/tests/fixtures/connected_account_input.json +++ b/tests/fixtures/connected_account_input.json @@ -1,4 +1,6 @@ { + "account_display_name": "Acme production", + "account_identifier": "workspace_123", "access_token": "gho_16C7e42F292c6912E7710c838347Ae178B4a", "refresh_token": "ghr_xxxxxxxxxxxxxxxxxxxx", "expires_at": "2025-12-31T23:59:59.000Z", diff --git a/tests/fixtures/connected_account_write_response.json b/tests/fixtures/connected_account_write_response.json new file mode 100644 index 00000000..8d620ad5 --- /dev/null +++ b/tests/fixtures/connected_account_write_response.json @@ -0,0 +1,31 @@ +{ + "object": "connected_account", + "id": "data_installation_01EHZNVPK3SFK441A1RGBFSHRT", + "connection_role": "compatibility", + "account_identifier": "workspace_123", + "account_display_name": "Acme production", + "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": null, + "scopes": [ + "repo", + "user:email" + ], + "auth_method": "oauth", + "api_key_last_4": null, + "client_id": "3MVG9dZJodJWxft2VoStSCVwPFsx0eDcpVc", + "client_secret_last_4": "cdef", + "config": { + "instance_url": "https://example.my.salesforce.com" + }, + "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": null, + "created_at": "2024-01-16T14:20:00.000Z", + "updated_at": "2024-01-16T14:20:00.000Z", + "warnings": [ + { + "code": "duplicate_account_display_name", + "message": "Another connection for this provider and owner has the same label." + } + ] +} diff --git a/tests/fixtures/connected_account_write_response_warning.json b/tests/fixtures/connected_account_write_response_warning.json new file mode 100644 index 00000000..cdababd1 --- /dev/null +++ b/tests/fixtures/connected_account_write_response_warning.json @@ -0,0 +1,4 @@ +{ + "code": "duplicate_account_display_name", + "message": "Another connection for this provider and owner has the same label." +} diff --git a/tests/fixtures/create_connected_account.json b/tests/fixtures/create_connected_account.json new file mode 100644 index 00000000..58d0459b --- /dev/null +++ b/tests/fixtures/create_connected_account.json @@ -0,0 +1,13 @@ +{ + "account_display_name": "Acme production", + "account_identifier": "workspace_123", + "access_token": "gho_16C7e42F292c6912E7710c838347Ae178B4a", + "refresh_token": "ghr_xxxxxxxxxxxxxxxxxxxx", + "expires_at": "2025-12-31T23:59:59.000Z", + "scopes": [ + "repo", + "user:email" + ], + "state": "connected", + "connection_intent": "add" +} diff --git a/tests/fixtures/create_organization_connected_account.json b/tests/fixtures/create_organization_connected_account.json new file mode 100644 index 00000000..c7c70483 --- /dev/null +++ b/tests/fixtures/create_organization_connected_account.json @@ -0,0 +1,14 @@ +{ + "account_display_name": "Acme production", + "account_identifier": "workspace_123", + "access_token": "gho_16C7e42F292c6912E7710c838347Ae178B4a", + "refresh_token": "ghr_xxxxxxxxxxxxxxxxxxxx", + "expires_at": "2025-12-31T23:59:59.000Z", + "scopes": [ + "repo", + "user:email" + ], + "state": "connected", + "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "connection_intent": "add" +} diff --git a/tests/fixtures/data_integrations_create_client_credentials_connection_request.json b/tests/fixtures/data_integrations_create_client_credentials_connection_request.json new file mode 100644 index 00000000..4cde367b --- /dev/null +++ b/tests/fixtures/data_integrations_create_client_credentials_connection_request.json @@ -0,0 +1,13 @@ +{ + "account_display_name": "Acme production", + "account_identifier": "workspace_123", + "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + "connection_owner": "user", + "client_id": "3MVG9...", + "client_secret": "shhh-secret", + "config": { + "salesforce_host": "acme.my.salesforce.com" + }, + "connection_intent": "add" +} diff --git a/tests/fixtures/data_integrations_list_response_data_connected_account.json b/tests/fixtures/data_integrations_list_response_data_connected_account.json index a6a8baf4..9df3f4b6 100644 --- a/tests/fixtures/data_integrations_list_response_data_connected_account.json +++ b/tests/fixtures/data_integrations_list_response_data_connected_account.json @@ -18,6 +18,8 @@ "instance_url": "https://example.my.salesforce.com" }, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": null, "created_at": "2024-01-16T14:20:00.000Z", "updated_at": "2024-01-16T14:20:00.000Z", "userlandUserId": "test_userlandUserId" diff --git a/tests/fixtures/organization_connected_account.json b/tests/fixtures/organization_connected_account.json new file mode 100644 index 00000000..69878884 --- /dev/null +++ b/tests/fixtures/organization_connected_account.json @@ -0,0 +1,13 @@ +{ + "account_display_name": "Acme production", + "account_identifier": "workspace_123", + "access_token": "gho_16C7e42F292c6912E7710c838347Ae178B4a", + "refresh_token": "ghr_xxxxxxxxxxxxxxxxxxxx", + "expires_at": "2025-12-31T23:59:59.000Z", + "scopes": [ + "repo", + "user:email" + ], + "state": "connected", + "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT" +} diff --git a/tests/fixtures/pipe_connected_account.json b/tests/fixtures/pipe_connected_account.json index 0a016bda..e181635a 100644 --- a/tests/fixtures/pipe_connected_account.json +++ b/tests/fixtures/pipe_connected_account.json @@ -15,6 +15,8 @@ "auth_method": "oauth", "api_key_last_4": null, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": null, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z" } diff --git a/tests/fixtures/pipes_account_connection_connected.json b/tests/fixtures/pipes_account_connection_connected.json index bd1caa7a..2f6bfa7e 100644 --- a/tests/fixtures/pipes_account_connection_connected.json +++ b/tests/fixtures/pipes_account_connection_connected.json @@ -19,6 +19,8 @@ "auth_method": "oauth", "api_key_last_4": null, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": null, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z" }, diff --git a/tests/fixtures/pipes_account_connection_disconnected.json b/tests/fixtures/pipes_account_connection_disconnected.json index 069add21..8fcca76f 100644 --- a/tests/fixtures/pipes_account_connection_disconnected.json +++ b/tests/fixtures/pipes_account_connection_disconnected.json @@ -19,6 +19,8 @@ "auth_method": "oauth", "api_key_last_4": null, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": null, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z" }, diff --git a/tests/fixtures/pipes_account_connection_reauthorization_needed.json b/tests/fixtures/pipes_account_connection_reauthorization_needed.json index 909f7965..111324b9 100644 --- a/tests/fixtures/pipes_account_connection_reauthorization_needed.json +++ b/tests/fixtures/pipes_account_connection_reauthorization_needed.json @@ -19,6 +19,8 @@ "auth_method": "oauth", "api_key_last_4": null, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": null, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z" }, diff --git a/tests/fixtures/pipes_connected_account_connected.json b/tests/fixtures/pipes_connected_account_connected.json index a4f73740..8d04c9eb 100644 --- a/tests/fixtures/pipes_connected_account_connected.json +++ b/tests/fixtures/pipes_connected_account_connected.json @@ -19,6 +19,8 @@ "auth_method": "oauth", "api_key_last_4": null, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": null, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z" }, diff --git a/tests/fixtures/pipes_connected_account_disconnected.json b/tests/fixtures/pipes_connected_account_disconnected.json index 46eb7279..046e3b80 100644 --- a/tests/fixtures/pipes_connected_account_disconnected.json +++ b/tests/fixtures/pipes_connected_account_disconnected.json @@ -19,6 +19,8 @@ "auth_method": "oauth", "api_key_last_4": null, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": null, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z" }, diff --git a/tests/fixtures/pipes_connected_account_reauthorization_needed.json b/tests/fixtures/pipes_connected_account_reauthorization_needed.json index 1afd4a7a..896181d6 100644 --- a/tests/fixtures/pipes_connected_account_reauthorization_needed.json +++ b/tests/fixtures/pipes_connected_account_reauthorization_needed.json @@ -19,6 +19,8 @@ "auth_method": "oauth", "api_key_last_4": null, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": null, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z" }, diff --git a/tests/test_pipes.py b/tests/test_pipes.py index a4b6d3c5..855654ac 100644 --- a/tests/test_pipes.py +++ b/tests/test_pipes.py @@ -17,6 +17,7 @@ from workos._pagination import AsyncPage, SyncPage from workos.common.models import ( ConnectedAccount, + ConnectedAccountWriteResponse, DataIntegrationsListResponse, PaginationOrder, ) @@ -107,29 +108,51 @@ def test_delete_data_integration(self, workos, httpx_mock): assert request.method == "DELETE" assert request.url.path.endswith("/data-integrations/test_slug") - def test_update_data_integration_api_key(self, workos, httpx_mock): + def test_create_data_integration_api_key(self, workos, httpx_mock): httpx_mock.add_response( - json=load_fixture("connected_account.json"), + json=load_fixture("connected_account_write_response.json"), ) - result = workos.pipes.update_data_integration_api_key( - "test_slug", user_id="test_user_id", secret="test_secret" + result = workos.pipes.create_data_integration_api_key( + "test_slug", + user_id="test_user_id", + secret="test_secret", + connection_intent="add", ) - assert isinstance(result, ConnectedAccount) + assert isinstance(result, ConnectedAccountWriteResponse) assert result.object == "connected_account" assert result.id == "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" request = httpx_mock.get_request() - assert request.method == "PUT" + assert request.method == "POST" assert request.url.path.endswith("/data-integrations/test_slug/api-key") body = json.loads(request.content) assert body["user_id"] == "test_user_id" assert body["secret"] == "test_secret" + assert body["connection_intent"] == "add" + + def test_update_data_integration_api_key(self, workos, httpx_mock): + httpx_mock.add_response( + json=load_fixture("connected_account_write_response.json"), + ) + result = workos.pipes.update_data_integration_api_key( + "test_slug", + body=load_fixture("data_integrations_upsert_api_key_request.json"), + ) + assert isinstance(result, ConnectedAccountWriteResponse) + assert result.object == "connected_account" + assert result.id == "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" + request = httpx_mock.get_request() + assert request.method == "PUT" + assert request.url.path.endswith("/data-integrations/test_slug/api-key") def test_authorize_data_integration(self, workos, httpx_mock): httpx_mock.add_response( json=load_fixture("data_integration_authorize_url_response.json"), ) result = workos.pipes.authorize_data_integration( - "test_slug", user_id="test_user_id" + "test_slug", + body=load_fixture( + "data_integrations_get_data_integration_authorize_url_request.json" + ), ) assert isinstance(result, DataIntegrationAuthorizeUrlResponse) assert ( @@ -139,24 +162,23 @@ def test_authorize_data_integration(self, workos, httpx_mock): request = httpx_mock.get_request() assert request.method == "POST" assert request.url.path.endswith("/data-integrations/test_slug/authorize") - body = json.loads(request.content) - assert body["user_id"] == "test_user_id" - def test_update_data_integration_client_credentials(self, workos, httpx_mock): + def test_create_data_integration_client_credential(self, workos, httpx_mock): httpx_mock.add_response( - json=load_fixture("connected_account.json"), + json=load_fixture("connected_account_write_response.json"), ) - result = workos.pipes.update_data_integration_client_credentials( + result = workos.pipes.create_data_integration_client_credential( "test_slug", user_id="test_user_id", client_id="test_client_id", client_secret="test_client_secret", + connection_intent="add", ) - assert isinstance(result, ConnectedAccount) + assert isinstance(result, ConnectedAccountWriteResponse) assert result.object == "connected_account" assert result.id == "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" request = httpx_mock.get_request() - assert request.method == "PUT" + assert request.method == "POST" assert request.url.path.endswith( "/data-integrations/test_slug/client-credentials" ) @@ -164,6 +186,26 @@ def test_update_data_integration_client_credentials(self, workos, httpx_mock): assert body["user_id"] == "test_user_id" assert body["client_id"] == "test_client_id" assert body["client_secret"] == "test_client_secret" + assert body["connection_intent"] == "add" + + def test_update_data_integration_client_credentials(self, workos, httpx_mock): + httpx_mock.add_response( + json=load_fixture("connected_account_write_response.json"), + ) + result = workos.pipes.update_data_integration_client_credentials( + "test_slug", + body=load_fixture( + "data_integrations_upsert_client_credentials_request.json" + ), + ) + assert isinstance(result, ConnectedAccountWriteResponse) + assert result.object == "connected_account" + assert result.id == "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" + request = httpx_mock.get_request() + assert request.method == "PUT" + assert request.url.path.endswith( + "/data-integrations/test_slug/client-credentials" + ) def test_create_data_integration_credential(self, workos, httpx_mock): httpx_mock.add_response( @@ -262,12 +304,12 @@ def test_get_organization_connected_account_encodes_query_params( def test_create_organization_connected_account(self, workos, httpx_mock): httpx_mock.add_response( - json=load_fixture("connected_account.json"), + json=load_fixture("connected_account_write_response.json"), ) result = workos.pipes.create_organization_connected_account( - "test_organization_id", "test_slug" + "test_organization_id", "test_slug", user_id="test_user_id" ) - assert isinstance(result, ConnectedAccount) + assert isinstance(result, ConnectedAccountWriteResponse) assert result.object == "connected_account" assert result.id == "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" request = httpx_mock.get_request() @@ -275,15 +317,17 @@ def test_create_organization_connected_account(self, workos, httpx_mock): assert request.url.path.endswith( "/organizations/test_organization_id/connected_accounts/test_slug" ) + body = json.loads(request.content) + assert body["user_id"] == "test_user_id" def test_update_organization_connected_account(self, workos, httpx_mock): httpx_mock.add_response( - json=load_fixture("connected_account.json"), + json=load_fixture("connected_account_write_response.json"), ) result = workos.pipes.update_organization_connected_account( - "test_organization_id", "test_slug" + "test_organization_id", "test_slug", user_id="test_user_id" ) - assert isinstance(result, ConnectedAccount) + assert isinstance(result, ConnectedAccountWriteResponse) assert result.object == "connected_account" assert result.id == "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" request = httpx_mock.get_request() @@ -291,16 +335,22 @@ def test_update_organization_connected_account(self, workos, httpx_mock): assert request.url.path.endswith( "/organizations/test_organization_id/connected_accounts/test_slug" ) + body = json.loads(request.content) + assert body["user_id"] == "test_user_id" def test_update_organization_connected_account_encodes_query_params( self, workos, httpx_mock ): - httpx_mock.add_response(json=load_fixture("connected_account.json")) + httpx_mock.add_response( + json=load_fixture("connected_account_write_response.json") + ) workos.pipes.update_organization_connected_account( "test_organization_id", "test_slug", + user_id="test_user_id", supports_multiple_connections=True, connected_account_id="value connected_account_id/test", + connection_intent="reauthorize", ) request = httpx_mock.get_request() assert request.url.params["supports_multiple_connections"] == "true" @@ -308,6 +358,7 @@ def test_update_organization_connected_account_encodes_query_params( request.url.params["connected_account_id"] == "value connected_account_id/test" ) + assert request.url.params["connection_intent"] == "reauthorize" def test_delete_organization_connected_account(self, workos, httpx_mock): httpx_mock.add_response(status_code=204) @@ -396,10 +447,10 @@ def test_get_user_connected_account_encodes_query_params(self, workos, httpx_moc def test_create_user_connected_account(self, workos, httpx_mock): httpx_mock.add_response( - json=load_fixture("connected_account.json"), + json=load_fixture("connected_account_write_response.json"), ) result = workos.pipes.create_user_connected_account("test_user_id", "test_slug") - assert isinstance(result, ConnectedAccount) + assert isinstance(result, ConnectedAccountWriteResponse) assert result.object == "connected_account" assert result.id == "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" request = httpx_mock.get_request() @@ -411,7 +462,9 @@ def test_create_user_connected_account(self, workos, httpx_mock): def test_create_user_connected_account_encodes_query_params( self, workos, httpx_mock ): - httpx_mock.add_response(json=load_fixture("connected_account.json")) + httpx_mock.add_response( + json=load_fixture("connected_account_write_response.json") + ) workos.pipes.create_user_connected_account( "test_user_id", "test_slug", organization_id="value organization_id/test" ) @@ -420,10 +473,10 @@ def test_create_user_connected_account_encodes_query_params( def test_update_user_connected_account(self, workos, httpx_mock): httpx_mock.add_response( - json=load_fixture("connected_account.json"), + json=load_fixture("connected_account_write_response.json"), ) result = workos.pipes.update_user_connected_account("test_user_id", "test_slug") - assert isinstance(result, ConnectedAccount) + assert isinstance(result, ConnectedAccountWriteResponse) assert result.object == "connected_account" assert result.id == "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" request = httpx_mock.get_request() @@ -435,13 +488,16 @@ def test_update_user_connected_account(self, workos, httpx_mock): def test_update_user_connected_account_encodes_query_params( self, workos, httpx_mock ): - httpx_mock.add_response(json=load_fixture("connected_account.json")) + httpx_mock.add_response( + json=load_fixture("connected_account_write_response.json") + ) workos.pipes.update_user_connected_account( "test_user_id", "test_slug", organization_id="value organization_id/test", supports_multiple_connections=True, connected_account_id="value connected_account_id/test", + connection_intent="reauthorize", ) request = httpx_mock.get_request() assert request.url.params["organization_id"] == "value organization_id/test" @@ -450,6 +506,7 @@ def test_update_user_connected_account_encodes_query_params( request.url.params["connected_account_id"] == "value connected_account_id/test" ) + assert request.url.params["connection_intent"] == "reauthorize" def test_delete_user_connected_account(self, workos, httpx_mock): httpx_mock.add_response(status_code=204) @@ -661,13 +718,34 @@ async def test_delete_data_integration(self, async_workos, httpx_mock): assert request.method == "DELETE" assert request.url.path.endswith("/data-integrations/test_slug") + @pytest.mark.asyncio + async def test_create_data_integration_api_key(self, async_workos, httpx_mock): + httpx_mock.add_response( + json=load_fixture("connected_account_write_response.json") + ) + result = await async_workos.pipes.create_data_integration_api_key( + "test_slug", + user_id="test_user_id", + secret="test_secret", + connection_intent="add", + ) + assert isinstance(result, ConnectedAccountWriteResponse) + assert result.object == "connected_account" + assert result.id == "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" + request = httpx_mock.get_request() + assert request.method == "POST" + assert request.url.path.endswith("/data-integrations/test_slug/api-key") + @pytest.mark.asyncio async def test_update_data_integration_api_key(self, async_workos, httpx_mock): - httpx_mock.add_response(json=load_fixture("connected_account.json")) + httpx_mock.add_response( + json=load_fixture("connected_account_write_response.json") + ) result = await async_workos.pipes.update_data_integration_api_key( - "test_slug", user_id="test_user_id", secret="test_secret" + "test_slug", + body=load_fixture("data_integrations_upsert_api_key_request.json"), ) - assert isinstance(result, ConnectedAccount) + assert isinstance(result, ConnectedAccountWriteResponse) assert result.object == "connected_account" assert result.id == "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" request = httpx_mock.get_request() @@ -680,7 +758,10 @@ async def test_authorize_data_integration(self, async_workos, httpx_mock): json=load_fixture("data_integration_authorize_url_response.json") ) result = await async_workos.pipes.authorize_data_integration( - "test_slug", user_id="test_user_id" + "test_slug", + body=load_fixture( + "data_integrations_get_data_integration_authorize_url_request.json" + ), ) assert isinstance(result, DataIntegrationAuthorizeUrlResponse) assert ( @@ -692,17 +773,42 @@ async def test_authorize_data_integration(self, async_workos, httpx_mock): assert request.url.path.endswith("/data-integrations/test_slug/authorize") @pytest.mark.asyncio - async def test_update_data_integration_client_credentials( + async def test_create_data_integration_client_credential( self, async_workos, httpx_mock ): - httpx_mock.add_response(json=load_fixture("connected_account.json")) - result = await async_workos.pipes.update_data_integration_client_credentials( + httpx_mock.add_response( + json=load_fixture("connected_account_write_response.json") + ) + result = await async_workos.pipes.create_data_integration_client_credential( "test_slug", user_id="test_user_id", client_id="test_client_id", client_secret="test_client_secret", + connection_intent="add", ) - assert isinstance(result, ConnectedAccount) + assert isinstance(result, ConnectedAccountWriteResponse) + assert result.object == "connected_account" + assert result.id == "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" + request = httpx_mock.get_request() + assert request.method == "POST" + assert request.url.path.endswith( + "/data-integrations/test_slug/client-credentials" + ) + + @pytest.mark.asyncio + async def test_update_data_integration_client_credentials( + self, async_workos, httpx_mock + ): + httpx_mock.add_response( + json=load_fixture("connected_account_write_response.json") + ) + result = await async_workos.pipes.update_data_integration_client_credentials( + "test_slug", + body=load_fixture( + "data_integrations_upsert_client_credentials_request.json" + ), + ) + assert isinstance(result, ConnectedAccountWriteResponse) assert result.object == "connected_account" assert result.id == "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" request = httpx_mock.get_request() @@ -815,11 +921,13 @@ async def test_get_organization_connected_account_encodes_query_params( async def test_create_organization_connected_account( self, async_workos, httpx_mock ): - httpx_mock.add_response(json=load_fixture("connected_account.json")) + httpx_mock.add_response( + json=load_fixture("connected_account_write_response.json") + ) result = await async_workos.pipes.create_organization_connected_account( - "test_organization_id", "test_slug" + "test_organization_id", "test_slug", user_id="test_user_id" ) - assert isinstance(result, ConnectedAccount) + assert isinstance(result, ConnectedAccountWriteResponse) assert result.object == "connected_account" assert result.id == "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" request = httpx_mock.get_request() @@ -832,11 +940,13 @@ async def test_create_organization_connected_account( async def test_update_organization_connected_account( self, async_workos, httpx_mock ): - httpx_mock.add_response(json=load_fixture("connected_account.json")) + httpx_mock.add_response( + json=load_fixture("connected_account_write_response.json") + ) result = await async_workos.pipes.update_organization_connected_account( - "test_organization_id", "test_slug" + "test_organization_id", "test_slug", user_id="test_user_id" ) - assert isinstance(result, ConnectedAccount) + assert isinstance(result, ConnectedAccountWriteResponse) assert result.object == "connected_account" assert result.id == "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" request = httpx_mock.get_request() @@ -849,12 +959,16 @@ async def test_update_organization_connected_account( async def test_update_organization_connected_account_encodes_query_params( self, async_workos, httpx_mock ): - httpx_mock.add_response(json=load_fixture("connected_account.json")) + httpx_mock.add_response( + json=load_fixture("connected_account_write_response.json") + ) await async_workos.pipes.update_organization_connected_account( "test_organization_id", "test_slug", + user_id="test_user_id", supports_multiple_connections=True, connected_account_id="value connected_account_id/test", + connection_intent="reauthorize", ) request = httpx_mock.get_request() assert request.url.params["supports_multiple_connections"] == "true" @@ -862,6 +976,7 @@ async def test_update_organization_connected_account_encodes_query_params( request.url.params["connected_account_id"] == "value connected_account_id/test" ) + assert request.url.params["connection_intent"] == "reauthorize" @pytest.mark.asyncio async def test_delete_organization_connected_account( @@ -962,11 +1077,13 @@ async def test_get_user_connected_account_encodes_query_params( @pytest.mark.asyncio async def test_create_user_connected_account(self, async_workos, httpx_mock): - httpx_mock.add_response(json=load_fixture("connected_account.json")) + httpx_mock.add_response( + json=load_fixture("connected_account_write_response.json") + ) result = await async_workos.pipes.create_user_connected_account( "test_user_id", "test_slug" ) - assert isinstance(result, ConnectedAccount) + assert isinstance(result, ConnectedAccountWriteResponse) assert result.object == "connected_account" assert result.id == "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" request = httpx_mock.get_request() @@ -979,7 +1096,9 @@ async def test_create_user_connected_account(self, async_workos, httpx_mock): async def test_create_user_connected_account_encodes_query_params( self, async_workos, httpx_mock ): - httpx_mock.add_response(json=load_fixture("connected_account.json")) + httpx_mock.add_response( + json=load_fixture("connected_account_write_response.json") + ) await async_workos.pipes.create_user_connected_account( "test_user_id", "test_slug", organization_id="value organization_id/test" ) @@ -988,11 +1107,13 @@ async def test_create_user_connected_account_encodes_query_params( @pytest.mark.asyncio async def test_update_user_connected_account(self, async_workos, httpx_mock): - httpx_mock.add_response(json=load_fixture("connected_account.json")) + httpx_mock.add_response( + json=load_fixture("connected_account_write_response.json") + ) result = await async_workos.pipes.update_user_connected_account( "test_user_id", "test_slug" ) - assert isinstance(result, ConnectedAccount) + assert isinstance(result, ConnectedAccountWriteResponse) assert result.object == "connected_account" assert result.id == "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" request = httpx_mock.get_request() @@ -1005,13 +1126,16 @@ async def test_update_user_connected_account(self, async_workos, httpx_mock): async def test_update_user_connected_account_encodes_query_params( self, async_workos, httpx_mock ): - httpx_mock.add_response(json=load_fixture("connected_account.json")) + httpx_mock.add_response( + json=load_fixture("connected_account_write_response.json") + ) await async_workos.pipes.update_user_connected_account( "test_user_id", "test_slug", organization_id="value organization_id/test", supports_multiple_connections=True, connected_account_id="value connected_account_id/test", + connection_intent="reauthorize", ) request = httpx_mock.get_request() assert request.url.params["organization_id"] == "value organization_id/test" @@ -1020,6 +1144,7 @@ async def test_update_user_connected_account_encodes_query_params( request.url.params["connected_account_id"] == "value connected_account_id/test" ) + assert request.url.params["connection_intent"] == "reauthorize" @pytest.mark.asyncio async def test_delete_user_connected_account(self, async_workos, httpx_mock): diff --git a/tests/test_pipes_models_round_trip.py b/tests/test_pipes_models_round_trip.py index 56ddf3f3..97ae92fc 100644 --- a/tests/test_pipes_models_round_trip.py +++ b/tests/test_pipes_models_round_trip.py @@ -6,6 +6,8 @@ from workos.pipes.models import ( ApiKeyInstallation, ConnectedAccount, + ConnectedAccountWriteResponse, + ConnectedAccountWriteResponseWarning, CustomProviderDefinition, DataIntegration, DataIntegrationAccessTokenResponse, @@ -14,12 +16,12 @@ DataIntegrationCredential, DataIntegrationCredentialsInput, DataIntegrationCredentialsResponse, - DataIntegrationCredentialsResponseCredential, DataIntegrationCustomProvider, DataIntegrationInstallation, DataIntegrationsListResponse, DataIntegrationsListResponseData, DataIntegrationsListResponseDataConnectedAccount, + DataIntegrationVendedCredential, UpdateCustomProviderDefinition, ) @@ -386,6 +388,48 @@ def test_data_integration_access_token_response_round_trips_unknown_enum_values( instance = DataIntegrationAccessTokenResponse.from_dict(data) assert instance.to_dict() == data + def test_data_integration_vended_credential_round_trip(self): + data = load_fixture("data_integration_vended_credential.json") + instance = DataIntegrationVendedCredential.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = DataIntegrationVendedCredential.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_data_integration_vended_credential_minimal_payload(self): + data = {} + instance = DataIntegrationVendedCredential.from_dict(data) + assert instance.to_dict() is not None + + def test_data_integration_vended_credential_omits_absent_optional_non_nullable_fields( + self, + ): + data = {"expires_at": "2025-12-31T23:59:59.000Z"} + instance = DataIntegrationVendedCredential.from_dict(data) + serialized = instance.to_dict() + assert "object" not in serialized + assert "auth_method" not in serialized + assert "value" not in serialized + assert "scopes" not in serialized + assert "missing_scopes" not in serialized + assert "config" not in serialized + assert "metadata" not in serialized + + def test_data_integration_vended_credential_preserves_nullable_fields(self): + data = { + "object": "credential", + "auth_method": "oauth", + "value": "gho_16C7e42F292c6912E7710c838347Ae178B4a", + "expires_at": None, + "scopes": ["repo", "user:email"], + "missing_scopes": [], + "config": {"account": "myorg-myaccount"}, + "metadata": {"instance_url": "https://acme.my.salesforce.com"}, + } + instance = DataIntegrationVendedCredential.from_dict(data) + serialized = instance.to_dict() + assert serialized["expires_at"] is None + def test_data_integration_credentials_response_round_trip(self): data = load_fixture("data_integration_credentials_response.json") instance = DataIntegrationCredentialsResponse.from_dict(data) @@ -421,21 +465,23 @@ def test_data_integration_credentials_response_round_trips_unknown_enum_values( "expires_at": "2025-12-31T23:59:59.000Z", "scopes": ["repo", "user:email"], "missing_scopes": [], + "config": {"account": "myorg-myaccount"}, + "metadata": {"instance_url": "https://acme.my.salesforce.com"}, }, "error": "unexpected_data_integration_credentials_response_error", } instance = DataIntegrationCredentialsResponse.from_dict(data) assert instance.to_dict() == data - def test_connected_account_round_trip(self): - data = load_fixture("connected_account.json") - instance = ConnectedAccount.from_dict(data) + def test_connected_account_write_response_round_trip(self): + data = load_fixture("connected_account_write_response.json") + instance = ConnectedAccountWriteResponse.from_dict(data) serialized = instance.to_dict() assert serialized == data - restored = ConnectedAccount.from_dict(serialized) + restored = ConnectedAccountWriteResponse.from_dict(serialized) assert restored.to_dict() == serialized - def test_connected_account_minimal_payload(self): + def test_connected_account_write_response_minimal_payload(self): data = { "object": "connected_account", "id": "data_installation_01EHZNVPK3SFK441A1RGBFSHRT", @@ -449,7 +495,7 @@ def test_connected_account_minimal_payload(self): "created_at": "2024-01-16T14:20:00.000Z", "updated_at": "2024-01-16T14:20:00.000Z", } - instance = ConnectedAccount.from_dict(data) + instance = ConnectedAccountWriteResponse.from_dict(data) serialized = instance.to_dict() assert serialized["object"] == data["object"] assert serialized["id"] == data["id"] @@ -463,7 +509,9 @@ def test_connected_account_minimal_payload(self): assert serialized["created_at"] == data["created_at"] assert serialized["updated_at"] == data["updated_at"] - def test_connected_account_omits_absent_optional_non_nullable_fields(self): + def test_connected_account_write_response_omits_absent_optional_non_nullable_fields( + self, + ): data = { "object": "connected_account", "id": "data_installation_01EHZNVPK3SFK441A1RGBFSHRT", @@ -477,15 +525,18 @@ def test_connected_account_omits_absent_optional_non_nullable_fields(self): "client_id": "3MVG9dZJodJWxft2VoStSCVwPFsx0eDcpVc", "client_secret_last_4": "cdef", "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": None, "created_at": "2024-01-16T14:20:00.000Z", "updated_at": "2024-01-16T14:20:00.000Z", } - instance = ConnectedAccount.from_dict(data) + instance = ConnectedAccountWriteResponse.from_dict(data) serialized = instance.to_dict() assert "auth_method" not in serialized assert "config" not in serialized + assert "warnings" not in serialized - def test_connected_account_preserves_nullable_fields(self): + def test_connected_account_write_response_preserves_nullable_fields(self): data = { "object": "connected_account", "id": "data_installation_01EHZNVPK3SFK441A1RGBFSHRT", @@ -501,10 +552,18 @@ def test_connected_account_preserves_nullable_fields(self): "client_secret_last_4": None, "config": {"instance_url": "https://example.my.salesforce.com"}, "state": "connected", + "established_by_user_id": None, + "state_reason": None, "created_at": "2024-01-16T14:20:00.000Z", "updated_at": "2024-01-16T14:20:00.000Z", + "warnings": [ + { + "code": "duplicate_account_display_name", + "message": "Another connection for this provider and owner has the same label.", + } + ], } - instance = ConnectedAccount.from_dict(data) + instance = ConnectedAccountWriteResponse.from_dict(data) serialized = instance.to_dict() assert serialized["account_identifier"] is None assert serialized["account_display_name"] is None @@ -513,12 +572,14 @@ def test_connected_account_preserves_nullable_fields(self): assert serialized["api_key_last_4"] is None assert serialized["client_id"] is None assert serialized["client_secret_last_4"] is None + assert serialized["established_by_user_id"] is None + assert serialized["state_reason"] is None - def test_connected_account_round_trips_unknown_enum_values(self): + def test_connected_account_write_response_round_trips_unknown_enum_values(self): data = { "object": "connected_account", "id": "data_installation_01EHZNVPK3SFK441A1RGBFSHRT", - "connection_role": "unexpected_connected_account_connection_role", + "connection_role": "unexpected_connected_account_write_response_connection_role", "account_identifier": "workspace_123", "account_display_name": "Acme production", "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", @@ -530,10 +591,18 @@ def test_connected_account_round_trips_unknown_enum_values(self): "client_secret_last_4": "cdef", "config": {"instance_url": "https://example.my.salesforce.com"}, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": None, "created_at": "2024-01-16T14:20:00.000Z", "updated_at": "2024-01-16T14:20:00.000Z", + "warnings": [ + { + "code": "duplicate_account_display_name", + "message": "Another connection for this provider and owner has the same label.", + } + ], } - instance = ConnectedAccount.from_dict(data) + instance = ConnectedAccountWriteResponse.from_dict(data) assert instance.to_dict() == data def test_data_integrations_list_response_round_trip(self): @@ -577,6 +646,8 @@ def test_data_integrations_list_response_minimal_payload(self): "client_secret_last_4": "cdef", "config": {"instance_url": "https://example.my.salesforce.com"}, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": None, "created_at": "2024-01-16T14:20:00.000Z", "updated_at": "2024-01-16T14:20:00.000Z", "userlandUserId": "test_userlandUserId", @@ -599,6 +670,8 @@ def test_data_integrations_list_response_minimal_payload(self): "instance_url": "https://example.my.salesforce.com" }, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": None, "created_at": "2024-01-16T14:20:00.000Z", "updated_at": "2024-01-16T14:20:00.000Z", "userlandUserId": "test_userlandUserId", @@ -612,6 +685,123 @@ def test_data_integrations_list_response_minimal_payload(self): assert serialized["object"] == data["object"] assert serialized["data"] == data["data"] + def test_connected_account_round_trip(self): + data = load_fixture("connected_account.json") + instance = ConnectedAccount.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = ConnectedAccount.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_connected_account_minimal_payload(self): + data = { + "object": "connected_account", + "id": "data_installation_01EHZNVPK3SFK441A1RGBFSHRT", + "connection_role": "compatibility", + "account_identifier": None, + "account_display_name": None, + "user_id": None, + "organization_id": None, + "scopes": ["repo", "user:email"], + "state": "connected", + "created_at": "2024-01-16T14:20:00.000Z", + "updated_at": "2024-01-16T14:20:00.000Z", + } + instance = ConnectedAccount.from_dict(data) + serialized = instance.to_dict() + assert serialized["object"] == data["object"] + assert serialized["id"] == data["id"] + assert serialized["connection_role"] == data["connection_role"] + assert serialized["account_identifier"] == data["account_identifier"] + assert serialized["account_display_name"] == data["account_display_name"] + assert serialized["user_id"] == data["user_id"] + assert serialized["organization_id"] == data["organization_id"] + assert serialized["scopes"] == data["scopes"] + assert serialized["state"] == data["state"] + assert serialized["created_at"] == data["created_at"] + assert serialized["updated_at"] == data["updated_at"] + + def test_connected_account_omits_absent_optional_non_nullable_fields(self): + data = { + "object": "connected_account", + "id": "data_installation_01EHZNVPK3SFK441A1RGBFSHRT", + "connection_role": "compatibility", + "account_identifier": "workspace_123", + "account_display_name": "Acme production", + "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": None, + "scopes": ["repo", "user:email"], + "api_key_last_4": None, + "client_id": "3MVG9dZJodJWxft2VoStSCVwPFsx0eDcpVc", + "client_secret_last_4": "cdef", + "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": None, + "created_at": "2024-01-16T14:20:00.000Z", + "updated_at": "2024-01-16T14:20:00.000Z", + } + instance = ConnectedAccount.from_dict(data) + serialized = instance.to_dict() + assert "auth_method" not in serialized + assert "config" not in serialized + + def test_connected_account_preserves_nullable_fields(self): + data = { + "object": "connected_account", + "id": "data_installation_01EHZNVPK3SFK441A1RGBFSHRT", + "connection_role": "compatibility", + "account_identifier": None, + "account_display_name": None, + "user_id": None, + "organization_id": None, + "scopes": ["repo", "user:email"], + "auth_method": "oauth", + "api_key_last_4": None, + "client_id": None, + "client_secret_last_4": None, + "config": {"instance_url": "https://example.my.salesforce.com"}, + "state": "connected", + "established_by_user_id": None, + "state_reason": None, + "created_at": "2024-01-16T14:20:00.000Z", + "updated_at": "2024-01-16T14:20:00.000Z", + } + instance = ConnectedAccount.from_dict(data) + serialized = instance.to_dict() + assert serialized["account_identifier"] is None + assert serialized["account_display_name"] is None + assert serialized["user_id"] is None + assert serialized["organization_id"] is None + assert serialized["api_key_last_4"] is None + assert serialized["client_id"] is None + assert serialized["client_secret_last_4"] is None + assert serialized["established_by_user_id"] is None + assert serialized["state_reason"] is None + + def test_connected_account_round_trips_unknown_enum_values(self): + data = { + "object": "connected_account", + "id": "data_installation_01EHZNVPK3SFK441A1RGBFSHRT", + "connection_role": "unexpected_connected_account_connection_role", + "account_identifier": "workspace_123", + "account_display_name": "Acme production", + "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": None, + "scopes": ["repo", "user:email"], + "auth_method": "oauth", + "api_key_last_4": None, + "client_id": "3MVG9dZJodJWxft2VoStSCVwPFsx0eDcpVc", + "client_secret_last_4": "cdef", + "config": {"instance_url": "https://example.my.salesforce.com"}, + "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": None, + "created_at": "2024-01-16T14:20:00.000Z", + "updated_at": "2024-01-16T14:20:00.000Z", + } + instance = ConnectedAccount.from_dict(data) + assert instance.to_dict() == data + def test_data_integrations_list_response_data_round_trip(self): data = load_fixture("data_integrations_list_response_data.json") instance = DataIntegrationsListResponseData.from_dict(data) @@ -651,6 +841,8 @@ def test_data_integrations_list_response_data_minimal_payload(self): "client_secret_last_4": "cdef", "config": {"instance_url": "https://example.my.salesforce.com"}, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": None, "created_at": "2024-01-16T14:20:00.000Z", "updated_at": "2024-01-16T14:20:00.000Z", "userlandUserId": "test_userlandUserId", @@ -705,6 +897,8 @@ def test_data_integrations_list_response_data_omits_absent_optional_non_nullable "client_secret_last_4": "cdef", "config": {"instance_url": "https://example.my.salesforce.com"}, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": None, "created_at": "2024-01-16T14:20:00.000Z", "updated_at": "2024-01-16T14:20:00.000Z", "userlandUserId": "test_userlandUserId", @@ -725,6 +919,8 @@ def test_data_integrations_list_response_data_omits_absent_optional_non_nullable "client_secret_last_4": "cdef", "config": {"instance_url": "https://example.my.salesforce.com"}, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": None, "created_at": "2024-01-16T14:20:00.000Z", "updated_at": "2024-01-16T14:20:00.000Z", "userlandUserId": "test_userlandUserId", @@ -767,6 +963,8 @@ def test_data_integrations_list_response_data_preserves_nullable_fields(self): "client_secret_last_4": "cdef", "config": {"instance_url": "https://example.my.salesforce.com"}, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": None, "created_at": "2024-01-16T14:20:00.000Z", "updated_at": "2024-01-16T14:20:00.000Z", "userlandUserId": "test_userlandUserId", @@ -809,6 +1007,8 @@ def test_data_integrations_list_response_data_round_trips_unknown_enum_values(se "client_secret_last_4": "cdef", "config": {"instance_url": "https://example.my.salesforce.com"}, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": None, "created_at": "2024-01-16T14:20:00.000Z", "updated_at": "2024-01-16T14:20:00.000Z", "userlandUserId": "test_userlandUserId", @@ -829,6 +1029,8 @@ def test_data_integrations_list_response_data_round_trips_unknown_enum_values(se "client_secret_last_4": "cdef", "config": {"instance_url": "https://example.my.salesforce.com"}, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": None, "created_at": "2024-01-16T14:20:00.000Z", "updated_at": "2024-01-16T14:20:00.000Z", "userlandUserId": "test_userlandUserId", @@ -838,46 +1040,23 @@ def test_data_integrations_list_response_data_round_trips_unknown_enum_values(se instance = DataIntegrationsListResponseData.from_dict(data) assert instance.to_dict() == data - def test_data_integration_credentials_response_credential_round_trip(self): - data = load_fixture("data_integration_credentials_response_credential.json") - instance = DataIntegrationCredentialsResponseCredential.from_dict(data) + def test_connected_account_write_response_warning_round_trip(self): + data = load_fixture("connected_account_write_response_warning.json") + instance = ConnectedAccountWriteResponseWarning.from_dict(data) serialized = instance.to_dict() assert serialized == data - restored = DataIntegrationCredentialsResponseCredential.from_dict(serialized) + restored = ConnectedAccountWriteResponseWarning.from_dict(serialized) assert restored.to_dict() == serialized - def test_data_integration_credentials_response_credential_minimal_payload(self): - data = { - "object": "credential", - "auth_method": "oauth", - "value": "gho_16C7e42F292c6912E7710c838347Ae178B4a", - "expires_at": None, - "scopes": ["repo", "user:email"], - "missing_scopes": [], - } - instance = DataIntegrationCredentialsResponseCredential.from_dict(data) - serialized = instance.to_dict() - assert serialized["object"] == data["object"] - assert serialized["auth_method"] == data["auth_method"] - assert serialized["value"] == data["value"] - assert serialized["expires_at"] == data["expires_at"] - assert serialized["scopes"] == data["scopes"] - assert serialized["missing_scopes"] == data["missing_scopes"] - - def test_data_integration_credentials_response_credential_preserves_nullable_fields( - self, - ): + def test_connected_account_write_response_warning_minimal_payload(self): data = { - "object": "credential", - "auth_method": "oauth", - "value": "gho_16C7e42F292c6912E7710c838347Ae178B4a", - "expires_at": None, - "scopes": ["repo", "user:email"], - "missing_scopes": [], + "code": "duplicate_account_display_name", + "message": "Another connection for this provider and owner has the same label.", } - instance = DataIntegrationCredentialsResponseCredential.from_dict(data) + instance = ConnectedAccountWriteResponseWarning.from_dict(data) serialized = instance.to_dict() - assert serialized["expires_at"] is None + assert serialized["code"] == data["code"] + assert serialized["message"] == data["message"] def test_data_integration_access_token_response_access_token_round_trip(self): data = load_fixture("data_integration_access_token_response_access_token.json") @@ -1144,6 +1323,8 @@ def test_data_integrations_list_response_data_connected_account_omits_absent_opt "client_id": "3MVG9dZJodJWxft2VoStSCVwPFsx0eDcpVc", "client_secret_last_4": "cdef", "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": None, "created_at": "2024-01-16T14:20:00.000Z", "updated_at": "2024-01-16T14:20:00.000Z", "userlandUserId": "test_userlandUserId", @@ -1171,6 +1352,8 @@ def test_data_integrations_list_response_data_connected_account_preserves_nullab "client_secret_last_4": None, "config": {"instance_url": "https://example.my.salesforce.com"}, "state": "connected", + "established_by_user_id": None, + "state_reason": None, "created_at": "2024-01-16T14:20:00.000Z", "updated_at": "2024-01-16T14:20:00.000Z", "userlandUserId": None, @@ -1184,6 +1367,8 @@ def test_data_integrations_list_response_data_connected_account_preserves_nullab assert serialized["api_key_last_4"] is None assert serialized["client_id"] is None assert serialized["client_secret_last_4"] is None + assert serialized["established_by_user_id"] is None + assert serialized["state_reason"] is None assert serialized["userlandUserId"] is None def test_data_integrations_list_response_data_connected_account_round_trips_unknown_enum_values( @@ -1204,6 +1389,8 @@ def test_data_integrations_list_response_data_connected_account_round_trips_unkn "client_secret_last_4": "cdef", "config": {"instance_url": "https://example.my.salesforce.com"}, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": None, "created_at": "2024-01-16T14:20:00.000Z", "updated_at": "2024-01-16T14:20:00.000Z", "userlandUserId": "test_userlandUserId", From 41172e6473712790494ce5e703b2b1595530d7c4 Mon Sep 17 00:00:00 2001 From: "workos-sdk-automation[bot]" <255426317+workos-sdk-automation[bot]@users.noreply.github.com> Date: Wed, 14 Oct 2026 21:31:32 +0000 Subject: [PATCH 6/8] feat(user_management): Update user management API surface --- src/workos/user_management/_resource.py | 4 ++-- src/workos/user_management/models/create_user.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/workos/user_management/_resource.py b/src/workos/user_management/_resource.py index 269df870..7293a2e9 100644 --- a/src/workos/user_management/_resource.py +++ b/src/workos/user_management/_resource.py @@ -1224,7 +1224,7 @@ def create_user( Create a new user in the current environment. Args: - email: The email address of the user. + email: The email address of the user. WorkOS trims whitespace and converts the address to lowercase before storing it. first_name: The first name of the user. last_name: The last name of the user. name: The user's full name. @@ -3976,7 +3976,7 @@ async def create_user( Create a new user in the current environment. Args: - email: The email address of the user. + email: The email address of the user. WorkOS trims whitespace and converts the address to lowercase before storing it. first_name: The first name of the user. last_name: The last name of the user. name: The user's full name. diff --git a/src/workos/user_management/models/create_user.py b/src/workos/user_management/models/create_user.py index 064704df..7a66f71f 100644 --- a/src/workos/user_management/models/create_user.py +++ b/src/workos/user_management/models/create_user.py @@ -20,7 +20,7 @@ class CreateUser: """Create User model.""" email: str - """The email address of the user.""" + """The email address of the user. WorkOS trims whitespace and converts the address to lowercase before storing it.""" first_name: str | None = None """The first name of the user.""" last_name: str | None = None From b21f7aaae47d2bacddfef18b5cd196966ab00da2 Mon Sep 17 00:00:00 2001 From: "workos-sdk-automation[bot]" <255426317+workos-sdk-automation[bot]@users.noreply.github.com> Date: Wed, 14 Oct 2026 21:31:32 +0000 Subject: [PATCH 7/8] chore(generated): regenerate shared files for AdminPortal, Agents, ApiKeys, AuditLogs, Authorization, ClientApi, Connect, DirectorySync, Events, FeatureFlags, Groups, MultiFactorAuth, OrganizationDomains, OrganizationMembership, Organizations, Pipes, PipesProvider, PlatformTeams, Radar, SSO, UserManagement, Vault, Webhooks, Widgets --- .last-synced-sha | 2 +- .oagen-manifest.json | 88 +++++++- src/workos/agents/_resource.py | 22 ++ src/workos/agents/models/__init__.py | 9 + src/workos/agents/models/agent_blueprint.py | 7 + .../models/agent_blueprint_invocable_as.py | 9 + .../models/agent_blueprints_update_request.py | 12 + ..._blueprints_update_request_invocable_as.py | 11 + src/workos/common/__init__.py | 63 +++++- src/workos/common/models/__init__.py | 145 ++++++------ ...t_blueprint_invocable_as_instance_types.py | 12 + ...ent_blueprint_updated_data_invocable_as.py | 9 + ...pdated_data_invocable_as_instance_types.py | 12 + ...ate_request_invocable_as_instance_types.py | 12 + .../agent_instance_deleted_data_type.py | 8 +- .../common/models/agent_instance_type.py | 6 +- .../common/models/connected_account_input.py | 63 ------ ...sert_api_key_request_2_connection_owner.py | 12 + ...upsert_api_key_request_connection_owner.py | 6 +- .../models/directory_sync_rate_limit_error.py | 40 ++++ ...upsert_api_key_request_connection_owner.py | 12 + src/workos/common/models/vault_dek_rekeyed.py | 57 +++++ .../common/models/vault_dek_rekeyed_data.py | 7 + .../vault_dek_rekeyed_data_actor_source.py | 8 + src/workos/directory_sync/_resource.py | 68 +++++- src/workos/directory_sync/models/__init__.py | 1 + .../models/directory_sync_response.py | 32 +++ src/workos/events/models/event_schema.py | 3 + .../organization_membership/_resource.py | 2 + src/workos/sso/_resource.py | 6 + src/workos/sso/models/token_query.py | 5 + tests/fixtures/agent_blueprint.json | 6 + .../agent_blueprint_invocable_as.json | 6 + tests/fixtures/agent_blueprint_updated.json | 6 + .../agent_blueprint_updated_data.json | 6 + ...t_blueprint_updated_data_invocable_as.json | 6 + .../agent_blueprints_update_request.json | 6 + ...lueprints_update_request_invocable_as.json | 6 + ...a_integrations_upsert_api_key_request.json | 3 +- ...integrations_upsert_api_key_request_2.json | 9 + .../directory_sync_rate_limit_error.json | 5 + tests/fixtures/directory_sync_response.json | 3 + tests/fixtures/portal_link_response.json | 2 +- ...a_integrations_upsert_api_key_request.json | 10 + tests/fixtures/token_query.json | 1 + tests/fixtures/vault_dek_rekeyed.json | 37 +++ tests/fixtures/vault_dek_rekeyed_data.json | 11 + tests/test_agents_models_round_trip.py | 62 ++++++ tests/test_common_models_round_trip.py | 210 ++++++++++++++++-- tests/test_directory_sync.py | 27 ++- .../test_directory_sync_models_round_trip.py | 15 ++ 51 files changed, 1015 insertions(+), 171 deletions(-) create mode 100644 src/workos/agents/models/agent_blueprint_invocable_as.py create mode 100644 src/workos/agents/models/agent_blueprints_update_request_invocable_as.py create mode 100644 src/workos/common/models/agent_blueprint_invocable_as_instance_types.py create mode 100644 src/workos/common/models/agent_blueprint_updated_data_invocable_as.py create mode 100644 src/workos/common/models/agent_blueprint_updated_data_invocable_as_instance_types.py create mode 100644 src/workos/common/models/agent_blueprints_update_request_invocable_as_instance_types.py delete mode 100644 src/workos/common/models/connected_account_input.py create mode 100644 src/workos/common/models/data_integrations_upsert_api_key_request_2_connection_owner.py create mode 100644 src/workos/common/models/directory_sync_rate_limit_error.py create mode 100644 src/workos/common/models/reauthorize_data_integrations_upsert_api_key_request_connection_owner.py create mode 100644 src/workos/common/models/vault_dek_rekeyed.py create mode 100644 src/workos/common/models/vault_dek_rekeyed_data.py create mode 100644 src/workos/common/models/vault_dek_rekeyed_data_actor_source.py create mode 100644 src/workos/directory_sync/models/directory_sync_response.py create mode 100644 tests/fixtures/agent_blueprint_invocable_as.json create mode 100644 tests/fixtures/agent_blueprint_updated_data_invocable_as.json create mode 100644 tests/fixtures/agent_blueprints_update_request_invocable_as.json create mode 100644 tests/fixtures/data_integrations_upsert_api_key_request_2.json create mode 100644 tests/fixtures/directory_sync_rate_limit_error.json create mode 100644 tests/fixtures/directory_sync_response.json create mode 100644 tests/fixtures/reauthorize_data_integrations_upsert_api_key_request.json create mode 100644 tests/fixtures/vault_dek_rekeyed.json create mode 100644 tests/fixtures/vault_dek_rekeyed_data.json diff --git a/.last-synced-sha b/.last-synced-sha index 89fbe35a..90399c99 100644 --- a/.last-synced-sha +++ b/.last-synced-sha @@ -1 +1 @@ -6f037208ce812063908f97744b4aaf4fdf570351 +81a34a1dfa516c1987aff4038af330436b53e151 diff --git a/.oagen-manifest.json b/.oagen-manifest.json index f04e001b..80065d9d 100644 --- a/.oagen-manifest.json +++ b/.oagen-manifest.json @@ -8,6 +8,7 @@ "src/workos/admin_portal/models/__init__.py", "src/workos/admin_portal/models/generate_link.py", "src/workos/admin_portal/models/portal_link_response.py", + "src/workos/admin_portal/models/revoke_link.py", "src/workos/agents/__init__.py", "src/workos/agents/_resource.py", "src/workos/agents/models/__init__.py", @@ -15,14 +16,17 @@ "src/workos/agents/models/agent_admin_link_claim_attempt_to_external_user_request_user.py", "src/workos/agents/models/agent_admin_validate_credential_request.py", "src/workos/agents/models/agent_blueprint.py", + "src/workos/agents/models/agent_blueprint_invocable_as.py", "src/workos/agents/models/agent_blueprint_invocable_by.py", "src/workos/agents/models/agent_blueprint_session_setting.py", "src/workos/agents/models/agent_blueprints_create_request.py", + "src/workos/agents/models/agent_blueprints_create_request_invocable_as.py", "src/workos/agents/models/agent_blueprints_create_request_invocable_by.py", "src/workos/agents/models/agent_blueprints_create_request_session_setting.py", "src/workos/agents/models/agent_blueprints_token_mint_token_request.py", "src/workos/agents/models/agent_blueprints_token_validate_token_request.py", "src/workos/agents/models/agent_blueprints_update_request.py", + "src/workos/agents/models/agent_blueprints_update_request_invocable_as.py", "src/workos/agents/models/agent_blueprints_update_request_invocable_by.py", "src/workos/agents/models/agent_blueprints_update_request_session_setting.py", "src/workos/agents/models/agent_credential_validation.py", @@ -113,19 +117,27 @@ "src/workos/common/models/action_user_registration_denied.py", "src/workos/common/models/action_user_registration_denied_data.py", "src/workos/common/models/actor.py", + "src/workos/common/models/add_data_integrations_get_data_integration_authorize_url_request_connection_owner.py", "src/workos/common/models/add_role_permission.py", "src/workos/common/models/agent_admin_validate_credential_request_type.py", "src/workos/common/models/agent_blueprint_created.py", "src/workos/common/models/agent_blueprint_created_data.py", + "src/workos/common/models/agent_blueprint_created_data_invocable_as.py", + "src/workos/common/models/agent_blueprint_created_data_invocable_as_instance_types.py", "src/workos/common/models/agent_blueprint_created_data_invocable_by.py", "src/workos/common/models/agent_blueprint_created_data_session_setting.py", "src/workos/common/models/agent_blueprint_deleted.py", "src/workos/common/models/agent_blueprint_deleted_data.py", + "src/workos/common/models/agent_blueprint_invocable_as_instance_types.py", "src/workos/common/models/agent_blueprint_updated.py", "src/workos/common/models/agent_blueprint_updated_data.py", + "src/workos/common/models/agent_blueprint_updated_data_invocable_as.py", + "src/workos/common/models/agent_blueprint_updated_data_invocable_as_instance_types.py", "src/workos/common/models/agent_blueprint_updated_data_invocable_by.py", "src/workos/common/models/agent_blueprint_updated_data_session_setting.py", + "src/workos/common/models/agent_blueprints_create_request_invocable_as_instance_types.py", "src/workos/common/models/agent_blueprints_token_mint_token_request_type.py", + "src/workos/common/models/agent_blueprints_update_request_invocable_as_instance_types.py", "src/workos/common/models/agent_instance_created.py", "src/workos/common/models/agent_instance_created_data.py", "src/workos/common/models/agent_instance_created_data_type.py", @@ -249,9 +261,13 @@ "src/workos/common/models/connected_account.py", "src/workos/common/models/connected_account_auth_method.py", "src/workos/common/models/connected_account_connection_role.py", - "src/workos/common/models/connected_account_input.py", "src/workos/common/models/connected_account_input_state.py", "src/workos/common/models/connected_account_state.py", + "src/workos/common/models/connected_account_write_response.py", + "src/workos/common/models/connected_account_write_response_auth_method.py", + "src/workos/common/models/connected_account_write_response_connection_role.py", + "src/workos/common/models/connected_account_write_response_state.py", + "src/workos/common/models/connected_account_write_response_warning.py", "src/workos/common/models/connection_activated.py", "src/workos/common/models/connection_activated_data.py", "src/workos/common/models/connection_activated_data_connection_type.py", @@ -281,10 +297,12 @@ "src/workos/common/models/connection_state.py", "src/workos/common/models/connection_status.py", "src/workos/common/models/connection_type.py", + "src/workos/common/models/create_connected_account_state.py", "src/workos/common/models/create_connection_oidc_options_id_token_signature_algorithm.py", "src/workos/common/models/create_connection_oidc_options_token_authentication_method.py", "src/workos/common/models/create_data_integration_auth_methods.py", "src/workos/common/models/create_data_integration_ownership.py", + "src/workos/common/models/create_organization_connected_account_state.py", "src/workos/common/models/create_user_invite_options_locale.py", "src/workos/common/models/create_user_password_hash_type.py", "src/workos/common/models/create_user_password_salt_position.py", @@ -300,6 +318,8 @@ "src/workos/common/models/data_integration_installation_connection_role.py", "src/workos/common/models/data_integration_ownership.py", "src/workos/common/models/data_integration_state.py", + "src/workos/common/models/data_integrations_create_api_key_connection_request_connection_owner.py", + "src/workos/common/models/data_integrations_create_client_credentials_connection_request_connection_owner.py", "src/workos/common/models/data_integrations_get_data_integration_authorize_url_request_connection_owner.py", "src/workos/common/models/data_integrations_get_user_token_request_connection_owner.py", "src/workos/common/models/data_integrations_list_response.py", @@ -311,11 +331,14 @@ "src/workos/common/models/data_integrations_list_response_data_connected_account_state.py", "src/workos/common/models/data_integrations_list_response_data_connection_owner.py", "src/workos/common/models/data_integrations_list_response_data_ownership.py", + "src/workos/common/models/data_integrations_upsert_api_key_request_2_connection_owner.py", "src/workos/common/models/data_integrations_upsert_api_key_request_connection_owner.py", + "src/workos/common/models/data_integrations_upsert_client_credentials_request_2_connection_owner.py", "src/workos/common/models/data_integrations_upsert_client_credentials_request_connection_owner.py", "src/workos/common/models/data_integrations_vend_credentials_request_connection_owner.py", "src/workos/common/models/directory_group.py", "src/workos/common/models/directory_state.py", + "src/workos/common/models/directory_sync_rate_limit_error.py", "src/workos/common/models/directory_type.py", "src/workos/common/models/directory_user.py", "src/workos/common/models/directory_user_email.py", @@ -422,6 +445,7 @@ "src/workos/common/models/list_metadata.py", "src/workos/common/models/magic_auth_created.py", "src/workos/common/models/magic_auth_created_data.py", + "src/workos/common/models/organization_connected_account_state.py", "src/workos/common/models/organization_created.py", "src/workos/common/models/organization_created_data.py", "src/workos/common/models/organization_created_data_domain.py", @@ -516,6 +540,9 @@ "src/workos/common/models/radar_standalone_response_blocklist_type.py", "src/workos/common/models/radar_standalone_response_control.py", "src/workos/common/models/radar_standalone_response_verdict.py", + "src/workos/common/models/reauthorize_data_integrations_get_data_integration_authorize_url_request_connection_owner.py", + "src/workos/common/models/reauthorize_data_integrations_upsert_api_key_request_connection_owner.py", + "src/workos/common/models/reauthorize_data_integrations_upsert_client_credentials_request_connection_owner.py", "src/workos/common/models/resend_user_invite_options_locale.py", "src/workos/common/models/resource_export_completed.py", "src/workos/common/models/resource_export_completed_data.py", @@ -595,6 +622,9 @@ "src/workos/common/models/vault_dek_read.py", "src/workos/common/models/vault_dek_read_data.py", "src/workos/common/models/vault_dek_read_data_actor_source.py", + "src/workos/common/models/vault_dek_rekeyed.py", + "src/workos/common/models/vault_dek_rekeyed_data.py", + "src/workos/common/models/vault_dek_rekeyed_data_actor_source.py", "src/workos/common/models/vault_kek_created.py", "src/workos/common/models/vault_kek_created_data.py", "src/workos/common/models/vault_kek_created_data_actor_source.py", @@ -637,6 +667,7 @@ "src/workos/directory_sync/models/directory.py", "src/workos/directory_sync/models/directory_metadata.py", "src/workos/directory_sync/models/directory_metadata_user.py", + "src/workos/directory_sync/models/directory_sync_response.py", "src/workos/directory_sync/models/directory_user_with_groups.py", "src/workos/directory_sync/models/directory_user_with_groups_email.py", "src/workos/events/__init__.py", @@ -696,8 +727,12 @@ "src/workos/pipes/__init__.py", "src/workos/pipes/_resource.py", "src/workos/pipes/models/__init__.py", + "src/workos/pipes/models/add_data_integrations_get_data_integration_authorize_url_request.py", "src/workos/pipes/models/api_key_installation.py", + "src/workos/pipes/models/connected_account_input.py", + "src/workos/pipes/models/create_connected_account.py", "src/workos/pipes/models/create_data_integration.py", + "src/workos/pipes/models/create_organization_connected_account.py", "src/workos/pipes/models/custom_provider_definition.py", "src/workos/pipes/models/data_integration.py", "src/workos/pipes/models/data_integration_access_token_response.py", @@ -706,15 +741,23 @@ "src/workos/pipes/models/data_integration_credential.py", "src/workos/pipes/models/data_integration_credentials_input.py", "src/workos/pipes/models/data_integration_credentials_response.py", - "src/workos/pipes/models/data_integration_credentials_response_credential.py", "src/workos/pipes/models/data_integration_custom_provider.py", "src/workos/pipes/models/data_integration_installation.py", + "src/workos/pipes/models/data_integration_vended_credential.py", + "src/workos/pipes/models/data_integrations_create_api_key_connection_request.py", + "src/workos/pipes/models/data_integrations_create_client_credentials_connection_request.py", "src/workos/pipes/models/data_integrations_get_data_integration_authorize_url_request.py", "src/workos/pipes/models/data_integrations_get_user_token_request.py", "src/workos/pipes/models/data_integrations_upsert_api_key_request.py", + "src/workos/pipes/models/data_integrations_upsert_api_key_request_2.py", "src/workos/pipes/models/data_integrations_upsert_client_credentials_request.py", + "src/workos/pipes/models/data_integrations_upsert_client_credentials_request_2.py", "src/workos/pipes/models/data_integrations_vend_credentials_request.py", + "src/workos/pipes/models/organization_connected_account.py", "src/workos/pipes/models/pipes_ownership.py", + "src/workos/pipes/models/reauthorize_data_integrations_get_data_integration_authorize_url_request.py", + "src/workos/pipes/models/reauthorize_data_integrations_upsert_api_key_request.py", + "src/workos/pipes/models/reauthorize_data_integrations_upsert_client_credentials_request.py", "src/workos/pipes/models/update_custom_provider_definition.py", "src/workos/pipes/models/update_data_integration.py", "src/workos/pipes_provider/__init__.py", @@ -901,6 +944,7 @@ "tests/fixtures/action_user_registration_denied.json", "tests/fixtures/action_user_registration_denied_data.json", "tests/fixtures/actor.json", + "tests/fixtures/add_data_integrations_get_data_integration_authorize_url_request.json", "tests/fixtures/add_role_permission.json", "tests/fixtures/agent_admin_link_claim_attempt_to_external_user_request.json", "tests/fixtures/agent_admin_link_claim_attempt_to_external_user_request_user.json", @@ -908,22 +952,27 @@ "tests/fixtures/agent_blueprint.json", "tests/fixtures/agent_blueprint_created.json", "tests/fixtures/agent_blueprint_created_data.json", + "tests/fixtures/agent_blueprint_created_data_invocable_as.json", "tests/fixtures/agent_blueprint_created_data_invocable_by.json", "tests/fixtures/agent_blueprint_created_data_session_setting.json", "tests/fixtures/agent_blueprint_deleted.json", "tests/fixtures/agent_blueprint_deleted_data.json", + "tests/fixtures/agent_blueprint_invocable_as.json", "tests/fixtures/agent_blueprint_invocable_by.json", "tests/fixtures/agent_blueprint_session_setting.json", "tests/fixtures/agent_blueprint_updated.json", "tests/fixtures/agent_blueprint_updated_data.json", + "tests/fixtures/agent_blueprint_updated_data_invocable_as.json", "tests/fixtures/agent_blueprint_updated_data_invocable_by.json", "tests/fixtures/agent_blueprint_updated_data_session_setting.json", "tests/fixtures/agent_blueprints_create_request.json", + "tests/fixtures/agent_blueprints_create_request_invocable_as.json", "tests/fixtures/agent_blueprints_create_request_invocable_by.json", "tests/fixtures/agent_blueprints_create_request_session_setting.json", "tests/fixtures/agent_blueprints_token_mint_token_request.json", "tests/fixtures/agent_blueprints_token_validate_token_request.json", "tests/fixtures/agent_blueprints_update_request.json", + "tests/fixtures/agent_blueprints_update_request_invocable_as.json", "tests/fixtures/agent_blueprints_update_request_invocable_by.json", "tests/fixtures/agent_blueprints_update_request_session_setting.json", "tests/fixtures/agent_credential_validation.json", @@ -1080,6 +1129,8 @@ "tests/fixtures/connect_application_oauth_redirect_uris.json", "tests/fixtures/connected_account.json", "tests/fixtures/connected_account_input.json", + "tests/fixtures/connected_account_write_response.json", + "tests/fixtures/connected_account_write_response_warning.json", "tests/fixtures/connection.json", "tests/fixtures/connection_activated.json", "tests/fixtures/connection_activated_data.json", @@ -1102,6 +1153,7 @@ "tests/fixtures/create_authkit_oauth_resource.json", "tests/fixtures/create_authorization_permission.json", "tests/fixtures/create_authorization_resource.json", + "tests/fixtures/create_connected_account.json", "tests/fixtures/create_connection.json", "tests/fixtures/create_connection_attribute_maps.json", "tests/fixtures/create_connection_key_pair.json", @@ -1121,6 +1173,7 @@ "tests/fixtures/create_oauth_application.json", "tests/fixtures/create_object_request.json", "tests/fixtures/create_organization_api_key.json", + "tests/fixtures/create_organization_connected_account.json", "tests/fixtures/create_organization_domain.json", "tests/fixtures/create_organization_role.json", "tests/fixtures/create_password_reset.json", @@ -1146,16 +1199,20 @@ "tests/fixtures/data_integration_credentials.json", "tests/fixtures/data_integration_credentials_input.json", "tests/fixtures/data_integration_credentials_response.json", - "tests/fixtures/data_integration_credentials_response_credential.json", "tests/fixtures/data_integration_custom_provider.json", "tests/fixtures/data_integration_installation.json", + "tests/fixtures/data_integration_vended_credential.json", + "tests/fixtures/data_integrations_create_api_key_connection_request.json", + "tests/fixtures/data_integrations_create_client_credentials_connection_request.json", "tests/fixtures/data_integrations_get_data_integration_authorize_url_request.json", "tests/fixtures/data_integrations_get_user_token_request.json", "tests/fixtures/data_integrations_list_response.json", "tests/fixtures/data_integrations_list_response_data.json", "tests/fixtures/data_integrations_list_response_data_connected_account.json", "tests/fixtures/data_integrations_upsert_api_key_request.json", + "tests/fixtures/data_integrations_upsert_api_key_request_2.json", "tests/fixtures/data_integrations_upsert_client_credentials_request.json", + "tests/fixtures/data_integrations_upsert_client_credentials_request_2.json", "tests/fixtures/data_integrations_vend_credentials_request.json", "tests/fixtures/decrypt_request.json", "tests/fixtures/decrypt_response.json", @@ -1167,6 +1224,8 @@ "tests/fixtures/directory_group.json", "tests/fixtures/directory_metadata.json", "tests/fixtures/directory_metadata_user.json", + "tests/fixtures/directory_sync_rate_limit_error.json", + "tests/fixtures/directory_sync_response.json", "tests/fixtures/directory_user.json", "tests/fixtures/directory_user_email.json", "tests/fixtures/directory_user_with_groups.json", @@ -1327,6 +1386,7 @@ "tests/fixtures/organization_api_key_with_value.json", "tests/fixtures/organization_api_key_with_value_owner.json", "tests/fixtures/organization_authorized_connect_application_list_data.json", + "tests/fixtures/organization_connected_account.json", "tests/fixtures/organization_created.json", "tests/fixtures/organization_created_data.json", "tests/fixtures/organization_created_data_domain.json", @@ -1408,6 +1468,9 @@ "tests/fixtures/radar_standalone_response.json", "tests/fixtures/radar_standalone_update_radar_attempt_request.json", "tests/fixtures/radar_standalone_update_radar_list_request.json", + "tests/fixtures/reauthorize_data_integrations_get_data_integration_authorize_url_request.json", + "tests/fixtures/reauthorize_data_integrations_upsert_api_key_request.json", + "tests/fixtures/reauthorize_data_integrations_upsert_client_credentials_request.json", "tests/fixtures/redirect_uri.json", "tests/fixtures/redirect_uri_input.json", "tests/fixtures/refresh_token_session_authenticate_request.json", @@ -1425,6 +1488,7 @@ "tests/fixtures/resource_export_downloaded_data.json", "tests/fixtures/resource_export_failed.json", "tests/fixtures/resource_export_failed_data.json", + "tests/fixtures/revoke_link.json", "tests/fixtures/revoke_session.json", "tests/fixtures/role.json", "tests/fixtures/role_created.json", @@ -1518,6 +1582,8 @@ "tests/fixtures/vault_dek_decrypted_data.json", "tests/fixtures/vault_dek_read.json", "tests/fixtures/vault_dek_read_data.json", + "tests/fixtures/vault_dek_rekeyed.json", + "tests/fixtures/vault_dek_rekeyed_data.json", "tests/fixtures/vault_kek_created.json", "tests/fixtures/vault_kek_created_data.json", "tests/fixtures/vault_kek_deleted.json", @@ -1970,6 +2036,10 @@ "sdkMethod": "delete_data_integration", "service": "pipes" }, + "POST /data-integrations/{slug}/api-key": { + "sdkMethod": "create_data_integration_api_key", + "service": "pipes" + }, "PUT /data-integrations/{slug}/api-key": { "sdkMethod": "update_data_integration_api_key", "service": "pipes" @@ -1978,6 +2048,10 @@ "sdkMethod": "authorize_data_integration", "service": "pipes" }, + "POST /data-integrations/{slug}/client-credentials": { + "sdkMethod": "create_data_integration_client_credential", + "service": "pipes" + }, "PUT /data-integrations/{slug}/client-credentials": { "sdkMethod": "update_data_integration_client_credentials", "service": "pipes" @@ -2014,6 +2088,10 @@ "sdkMethod": "delete_directory", "service": "directory_sync" }, + "POST /directories/{id}/sync": { + "sdkMethod": "create_directory_sync", + "service": "directory_sync" + }, "GET /directory_groups": { "sdkMethod": "list_groups", "service": "directory_sync" @@ -2218,6 +2296,10 @@ "sdkMethod": "generate_link", "service": "admin_portal" }, + "POST /portal/revoke_link": { + "sdkMethod": "create_revoke_link", + "service": "admin_portal" + }, "POST /radar/attempts": { "sdkMethod": "create_attempt", "service": "radar" diff --git a/src/workos/agents/_resource.py b/src/workos/agents/_resource.py index a9325f5f..8f962d4e 100644 --- a/src/workos/agents/_resource.py +++ b/src/workos/agents/_resource.py @@ -20,8 +20,10 @@ from .models import ( AgentAdminLinkClaimAttemptToExternalUserRequestUser, AgentBlueprint, + AgentBlueprintsCreateRequestInvocableAs, AgentBlueprintsCreateRequestInvocableBy, AgentBlueprintsCreateRequestSessionSetting, + AgentBlueprintsUpdateRequestInvocableAs, AgentBlueprintsUpdateRequestInvocableBy, AgentBlueprintsUpdateRequestSessionSetting, AgentCredentialValidation, @@ -93,6 +95,7 @@ def create_blueprint( description: str | None = None, permissions: list[str] | None = None, invocable_by: AgentBlueprintsCreateRequestInvocableBy | None = None, + invocable_as: AgentBlueprintsCreateRequestInvocableAs | None = None, session_settings: AgentBlueprintsCreateRequestSessionSetting | None = None, request_options: RequestOptions | None = None, ) -> AgentBlueprint: @@ -105,6 +108,7 @@ def create_blueprint( description: Human-readable description of the agent blueprint. permissions: Permission slugs forming the ceiling on what sessions minted from this blueprint may do. Each slug must exist in the environment. invocable_by: Who may mint sessions from this blueprint. + invocable_as: How sessions may be minted from this blueprint. session_settings: Token and session lifetimes for sessions minted from this blueprint. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. @@ -128,6 +132,9 @@ def create_blueprint( "invocable_by": invocable_by.to_dict() if invocable_by is not None else None, + "invocable_as": invocable_as.to_dict() + if invocable_as is not None + else None, "session_settings": session_settings.to_dict() if session_settings is not None else None, @@ -180,6 +187,7 @@ def update_blueprint( description: str | None | NotGiven = NOT_GIVEN, permissions: list[str] | None = None, invocable_by: AgentBlueprintsUpdateRequestInvocableBy | None = None, + invocable_as: AgentBlueprintsUpdateRequestInvocableAs | None = None, session_settings: AgentBlueprintsUpdateRequestSessionSetting | None = None, request_options: RequestOptions | None = None, ) -> AgentBlueprint: @@ -193,6 +201,7 @@ def update_blueprint( description: Human-readable description of the agent blueprint. Pass `null` to clear it. permissions: Permission slugs forming the ceiling on what sessions minted from this blueprint may do. Each slug must exist in the environment. invocable_by: Who may mint sessions from this blueprint. Omitted lists are left unchanged. + invocable_as: How sessions may be minted from this blueprint. Omitted lists are left unchanged. session_settings: Token and session lifetimes for sessions minted from this blueprint. Omitted fields are left unchanged. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. @@ -216,6 +225,9 @@ def update_blueprint( "invocable_by": invocable_by.to_dict() if invocable_by is not None else None, + "invocable_as": invocable_as.to_dict() + if invocable_as is not None + else None, "session_settings": session_settings.to_dict() if session_settings is not None else None, @@ -765,6 +777,7 @@ async def create_blueprint( description: str | None = None, permissions: list[str] | None = None, invocable_by: AgentBlueprintsCreateRequestInvocableBy | None = None, + invocable_as: AgentBlueprintsCreateRequestInvocableAs | None = None, session_settings: AgentBlueprintsCreateRequestSessionSetting | None = None, request_options: RequestOptions | None = None, ) -> AgentBlueprint: @@ -777,6 +790,7 @@ async def create_blueprint( description: Human-readable description of the agent blueprint. permissions: Permission slugs forming the ceiling on what sessions minted from this blueprint may do. Each slug must exist in the environment. invocable_by: Who may mint sessions from this blueprint. + invocable_as: How sessions may be minted from this blueprint. session_settings: Token and session lifetimes for sessions minted from this blueprint. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. @@ -800,6 +814,9 @@ async def create_blueprint( "invocable_by": invocable_by.to_dict() if invocable_by is not None else None, + "invocable_as": invocable_as.to_dict() + if invocable_as is not None + else None, "session_settings": session_settings.to_dict() if session_settings is not None else None, @@ -852,6 +869,7 @@ async def update_blueprint( description: str | None | NotGiven = NOT_GIVEN, permissions: list[str] | None = None, invocable_by: AgentBlueprintsUpdateRequestInvocableBy | None = None, + invocable_as: AgentBlueprintsUpdateRequestInvocableAs | None = None, session_settings: AgentBlueprintsUpdateRequestSessionSetting | None = None, request_options: RequestOptions | None = None, ) -> AgentBlueprint: @@ -865,6 +883,7 @@ async def update_blueprint( description: Human-readable description of the agent blueprint. Pass `null` to clear it. permissions: Permission slugs forming the ceiling on what sessions minted from this blueprint may do. Each slug must exist in the environment. invocable_by: Who may mint sessions from this blueprint. Omitted lists are left unchanged. + invocable_as: How sessions may be minted from this blueprint. Omitted lists are left unchanged. session_settings: Token and session lifetimes for sessions minted from this blueprint. Omitted fields are left unchanged. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. @@ -888,6 +907,9 @@ async def update_blueprint( "invocable_by": invocable_by.to_dict() if invocable_by is not None else None, + "invocable_as": invocable_as.to_dict() + if invocable_as is not None + else None, "session_settings": session_settings.to_dict() if session_settings is not None else None, diff --git a/src/workos/agents/models/__init__.py b/src/workos/agents/models/__init__.py index d1ddc67c..30567825 100644 --- a/src/workos/agents/models/__init__.py +++ b/src/workos/agents/models/__init__.py @@ -12,6 +12,9 @@ AgentAdminValidateCredentialRequest as AgentAdminValidateCredentialRequest, ) from .agent_blueprint import AgentBlueprint as AgentBlueprint +from .agent_blueprint_invocable_as import ( + AgentBlueprintInvocableAs as AgentBlueprintInvocableAs, +) from .agent_blueprint_invocable_by import ( AgentBlueprintInvocableBy as AgentBlueprintInvocableBy, ) @@ -21,6 +24,9 @@ from .agent_blueprints_create_request import ( AgentBlueprintsCreateRequest as AgentBlueprintsCreateRequest, ) +from .agent_blueprints_create_request_invocable_as import ( + AgentBlueprintsCreateRequestInvocableAs as AgentBlueprintsCreateRequestInvocableAs, +) from .agent_blueprints_create_request_invocable_by import ( AgentBlueprintsCreateRequestInvocableBy as AgentBlueprintsCreateRequestInvocableBy, ) @@ -36,6 +42,9 @@ from .agent_blueprints_update_request import ( AgentBlueprintsUpdateRequest as AgentBlueprintsUpdateRequest, ) +from .agent_blueprints_update_request_invocable_as import ( + AgentBlueprintsUpdateRequestInvocableAs as AgentBlueprintsUpdateRequestInvocableAs, +) from .agent_blueprints_update_request_invocable_by import ( AgentBlueprintsUpdateRequestInvocableBy as AgentBlueprintsUpdateRequestInvocableBy, ) diff --git a/src/workos/agents/models/agent_blueprint.py b/src/workos/agents/models/agent_blueprint.py index 60631320..922387a4 100644 --- a/src/workos/agents/models/agent_blueprint.py +++ b/src/workos/agents/models/agent_blueprint.py @@ -8,6 +8,7 @@ from workos._types import _format_datetime, _parse_datetime, _raise_deserialize_error +from .agent_blueprint_invocable_as import AgentBlueprintInvocableAs from .agent_blueprint_invocable_by import AgentBlueprintInvocableBy from .agent_blueprint_session_setting import AgentBlueprintSessionSetting @@ -28,6 +29,8 @@ class AgentBlueprint: """Permission slugs forming the ceiling on what sessions minted from this blueprint may do.""" invocable_by: AgentBlueprintInvocableBy """Who may mint sessions from this blueprint.""" + invocable_as: AgentBlueprintInvocableAs + """How sessions may be minted from this blueprint.""" session_settings: AgentBlueprintSessionSetting """Token and session lifetimes for sessions minted from this blueprint.""" created_at: datetime @@ -48,6 +51,9 @@ def from_dict(cls, data: dict[str, Any]) -> AgentBlueprint: invocable_by=AgentBlueprintInvocableBy.from_dict( cast(dict[str, Any], data["invocable_by"]) ), + invocable_as=AgentBlueprintInvocableAs.from_dict( + cast(dict[str, Any], data["invocable_as"]) + ), session_settings=AgentBlueprintSessionSetting.from_dict( cast(dict[str, Any], data["session_settings"]) ), @@ -69,6 +75,7 @@ def to_dict(self) -> dict[str, Any]: result["description"] = None result["permissions"] = self.permissions result["invocable_by"] = self.invocable_by.to_dict() + result["invocable_as"] = self.invocable_as.to_dict() result["session_settings"] = self.session_settings.to_dict() result["created_at"] = _format_datetime(self.created_at) result["updated_at"] = _format_datetime(self.updated_at) diff --git a/src/workos/agents/models/agent_blueprint_invocable_as.py b/src/workos/agents/models/agent_blueprint_invocable_as.py new file mode 100644 index 00000000..669ede07 --- /dev/null +++ b/src/workos/agents/models/agent_blueprint_invocable_as.py @@ -0,0 +1,9 @@ +# This file is auto-generated by oagen. Do not edit. + +from typing import TypeAlias + +from workos.common.models.agent_blueprint_created_data_invocable_as import ( + AgentBlueprintCreatedDataInvocableAs, +) + +AgentBlueprintInvocableAs: TypeAlias = AgentBlueprintCreatedDataInvocableAs diff --git a/src/workos/agents/models/agent_blueprints_update_request.py b/src/workos/agents/models/agent_blueprints_update_request.py index 4534d88f..3b9d1e0b 100644 --- a/src/workos/agents/models/agent_blueprints_update_request.py +++ b/src/workos/agents/models/agent_blueprints_update_request.py @@ -7,6 +7,9 @@ from workos._types import _raise_deserialize_error +from .agent_blueprints_update_request_invocable_as import ( + AgentBlueprintsUpdateRequestInvocableAs, +) from .agent_blueprints_update_request_invocable_by import ( AgentBlueprintsUpdateRequestInvocableBy, ) @@ -27,6 +30,8 @@ class AgentBlueprintsUpdateRequest: """Permission slugs forming the ceiling on what sessions minted from this blueprint may do. Each slug must exist in the environment.""" invocable_by: AgentBlueprintsUpdateRequestInvocableBy | None = None """Who may mint sessions from this blueprint. Omitted lists are left unchanged.""" + invocable_as: AgentBlueprintsUpdateRequestInvocableAs | None = None + """How sessions may be minted from this blueprint. Omitted lists are left unchanged.""" session_settings: AgentBlueprintsUpdateRequestSessionSetting | None = None """Token and session lifetimes for sessions minted from this blueprint. Omitted fields are left unchanged.""" @@ -43,6 +48,11 @@ def from_dict(cls, data: dict[str, Any]) -> AgentBlueprintsUpdateRequest: ) if (_v_invocable_by := data.get("invocable_by")) is not None else None, + invocable_as=AgentBlueprintsUpdateRequestInvocableAs.from_dict( + cast(dict[str, Any], _v_invocable_as) + ) + if (_v_invocable_as := data.get("invocable_as")) is not None + else None, session_settings=AgentBlueprintsUpdateRequestSessionSetting.from_dict( cast(dict[str, Any], _v_session_settings) ) @@ -65,6 +75,8 @@ def to_dict(self) -> dict[str, Any]: result["permissions"] = self.permissions if self.invocable_by is not None: result["invocable_by"] = self.invocable_by.to_dict() + if self.invocable_as is not None: + result["invocable_as"] = self.invocable_as.to_dict() if self.session_settings is not None: result["session_settings"] = self.session_settings.to_dict() return result diff --git a/src/workos/agents/models/agent_blueprints_update_request_invocable_as.py b/src/workos/agents/models/agent_blueprints_update_request_invocable_as.py new file mode 100644 index 00000000..63cdc6de --- /dev/null +++ b/src/workos/agents/models/agent_blueprints_update_request_invocable_as.py @@ -0,0 +1,11 @@ +# This file is auto-generated by oagen. Do not edit. + +from typing import TypeAlias + +from .agent_blueprints_create_request_invocable_as import ( + AgentBlueprintsCreateRequestInvocableAs, +) + +AgentBlueprintsUpdateRequestInvocableAs: TypeAlias = ( + AgentBlueprintsCreateRequestInvocableAs +) diff --git a/src/workos/common/__init__.py b/src/workos/common/__init__.py index 134eadb4..ec1968c6 100644 --- a/src/workos/common/__init__.py +++ b/src/workos/common/__init__.py @@ -1,6 +1,5 @@ # This file is auto-generated by oagen. Do not edit. -from .models import * from .models import ( AccessTokenAgentRegistrationCredentialIssuedDataDetail as AccessTokenAgentRegistrationCredentialIssuedDataDetail, ) @@ -10,12 +9,18 @@ from .models import ActionUserRegistrationDenied as ActionUserRegistrationDenied from .models import ActionUserRegistrationDeniedData as ActionUserRegistrationDeniedData from .models import Actor as Actor +from .models import ( + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner as AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner, +) from .models import AddRolePermission as AddRolePermission from .models import ( AgentAdminValidateCredentialRequestType as AgentAdminValidateCredentialRequestType, ) from .models import AgentBlueprintCreated as AgentBlueprintCreated from .models import AgentBlueprintCreatedData as AgentBlueprintCreatedData +from .models import ( + AgentBlueprintCreatedDataInvocableAs as AgentBlueprintCreatedDataInvocableAs, +) from .models import ( AgentBlueprintCreatedDataInvocableBy as AgentBlueprintCreatedDataInvocableBy, ) @@ -24,11 +29,23 @@ ) from .models import AgentBlueprintDeleted as AgentBlueprintDeleted from .models import AgentBlueprintDeletedData as AgentBlueprintDeletedData +from .models import ( + AgentBlueprintInvocableAsInstanceTypes as AgentBlueprintInvocableAsInstanceTypes, +) +from .models import ( + AgentBlueprintsCreateRequestInvocableAsInstanceTypes as AgentBlueprintsCreateRequestInvocableAsInstanceTypes, +) from .models import ( AgentBlueprintsTokenMintTokenRequestType as AgentBlueprintsTokenMintTokenRequestType, ) +from .models import ( + AgentBlueprintsUpdateRequestInvocableAsInstanceTypes as AgentBlueprintsUpdateRequestInvocableAsInstanceTypes, +) from .models import AgentBlueprintUpdated as AgentBlueprintUpdated from .models import AgentBlueprintUpdatedData as AgentBlueprintUpdatedData +from .models import ( + AgentBlueprintUpdatedDataInvocableAs as AgentBlueprintUpdatedDataInvocableAs, +) from .models import ( AgentBlueprintUpdatedDataInvocableBy as AgentBlueprintUpdatedDataInvocableBy, ) @@ -224,6 +241,19 @@ from .models import ConnectedAccountConnectionRole as ConnectedAccountConnectionRole from .models import ConnectedAccountInputState as ConnectedAccountInputState from .models import ConnectedAccountState as ConnectedAccountState +from .models import ConnectedAccountWriteResponse as ConnectedAccountWriteResponse +from .models import ( + ConnectedAccountWriteResponseAuthMethod as ConnectedAccountWriteResponseAuthMethod, +) +from .models import ( + ConnectedAccountWriteResponseConnectionRole as ConnectedAccountWriteResponseConnectionRole, +) +from .models import ( + ConnectedAccountWriteResponseState as ConnectedAccountWriteResponseState, +) +from .models import ( + ConnectedAccountWriteResponseWarning as ConnectedAccountWriteResponseWarning, +) from .models import ConnectionActivated as ConnectionActivated from .models import ConnectionActivatedData as ConnectionActivatedData from .models import ConnectionActivatedDataDomain as ConnectionActivatedDataDomain @@ -257,6 +287,7 @@ from .models import ConnectionState as ConnectionState from .models import ConnectionStatus as ConnectionStatus from .models import ConnectionType as ConnectionType +from .models import CreateConnectedAccountState as CreateConnectedAccountState from .models import ( CreateConnectionOIDCOptionsIdTokenSignatureAlgorithm as CreateConnectionOIDCOptionsIdTokenSignatureAlgorithm, ) @@ -265,6 +296,9 @@ ) from .models import CreateDataIntegrationAuthMethods as CreateDataIntegrationAuthMethods from .models import CreateDataIntegrationOwnership as CreateDataIntegrationOwnership +from .models import ( + CreateOrganizationConnectedAccountState as CreateOrganizationConnectedAccountState, +) from .models import CreateUserInviteOptionsLocale as CreateUserInviteOptionsLocale from .models import CreateUserPasswordHashType as CreateUserPasswordHashType from .models import CreateUserPasswordSaltPosition as CreateUserPasswordSaltPosition @@ -293,6 +327,12 @@ DataIntegrationInstallationConnectionRole as DataIntegrationInstallationConnectionRole, ) from .models import DataIntegrationOwnership as DataIntegrationOwnership +from .models import ( + DataIntegrationsCreateApiKeyConnectionRequestConnectionOwner as DataIntegrationsCreateApiKeyConnectionRequestConnectionOwner, +) +from .models import ( + DataIntegrationsCreateClientCredentialsConnectionRequestConnectionOwner as DataIntegrationsCreateClientCredentialsConnectionRequestConnectionOwner, +) from .models import ( DataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner as DataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner, ) @@ -323,9 +363,15 @@ DataIntegrationsListResponseDataOwnership as DataIntegrationsListResponseDataOwnership, ) from .models import DataIntegrationState as DataIntegrationState +from .models import ( + DataIntegrationsUpsertApiKeyRequest2ConnectionOwner as DataIntegrationsUpsertApiKeyRequest2ConnectionOwner, +) from .models import ( DataIntegrationsUpsertApiKeyRequestConnectionOwner as DataIntegrationsUpsertApiKeyRequestConnectionOwner, ) +from .models import ( + DataIntegrationsUpsertClientCredentialsRequest2ConnectionOwner as DataIntegrationsUpsertClientCredentialsRequest2ConnectionOwner, +) from .models import ( DataIntegrationsUpsertClientCredentialsRequestConnectionOwner as DataIntegrationsUpsertClientCredentialsRequestConnectionOwner, ) @@ -334,6 +380,7 @@ ) from .models import DirectoryGroup as DirectoryGroup from .models import DirectoryState as DirectoryState +from .models import DirectorySyncRateLimitError as DirectorySyncRateLimitError from .models import DirectoryType as DirectoryType from .models import DirectoryUser as DirectoryUser from .models import DirectoryUserEmail as DirectoryUserEmail @@ -447,6 +494,9 @@ from .models import ListMetadata as ListMetadata from .models import MagicAuthCreated as MagicAuthCreated from .models import MagicAuthCreatedData as MagicAuthCreatedData +from .models import ( + OrganizationConnectedAccountState as OrganizationConnectedAccountState, +) from .models import OrganizationCreated as OrganizationCreated from .models import OrganizationCreatedData as OrganizationCreatedData from .models import OrganizationCreatedDataDomain as OrganizationCreatedDataDomain @@ -560,6 +610,15 @@ ) from .models import RadarStandaloneResponseControl as RadarStandaloneResponseControl from .models import RadarStandaloneResponseVerdict as RadarStandaloneResponseVerdict +from .models import ( + ReauthorizeDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner as ReauthorizeDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner, +) +from .models import ( + ReauthorizeDataIntegrationsUpsertApiKeyRequestConnectionOwner as ReauthorizeDataIntegrationsUpsertApiKeyRequestConnectionOwner, +) +from .models import ( + ReauthorizeDataIntegrationsUpsertClientCredentialsRequestConnectionOwner as ReauthorizeDataIntegrationsUpsertClientCredentialsRequestConnectionOwner, +) from .models import ResendUserInviteOptionsLocale as ResendUserInviteOptionsLocale from .models import ResourceExportCompleted as ResourceExportCompleted from .models import ResourceExportCompletedData as ResourceExportCompletedData @@ -636,6 +695,8 @@ from .models import VaultDekDecryptedData as VaultDekDecryptedData from .models import VaultDekRead as VaultDekRead from .models import VaultDekReadData as VaultDekReadData +from .models import VaultDekRekeyed as VaultDekRekeyed +from .models import VaultDekRekeyedData as VaultDekRekeyedData from .models import VaultKekCreated as VaultKekCreated from .models import VaultKekCreatedData as VaultKekCreatedData from .models import VaultKekDeleted as VaultKekDeleted diff --git a/src/workos/common/models/__init__.py b/src/workos/common/models/__init__.py index 9e76b7e4..1f9ea596 100644 --- a/src/workos/common/models/__init__.py +++ b/src/workos/common/models/__init__.py @@ -19,6 +19,9 @@ ActionUserRegistrationDeniedData as ActionUserRegistrationDeniedData, ) from .actor import Actor as Actor +from .add_data_integrations_get_data_integration_authorize_url_request_connection_owner import ( + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner as AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner, +) from .add_role_permission import AddRolePermission as AddRolePermission from .agent_admin_validate_credential_request_type import ( AgentAdminValidateCredentialRequestType as AgentAdminValidateCredentialRequestType, @@ -27,6 +30,9 @@ from .agent_blueprint_created_data import ( AgentBlueprintCreatedData as AgentBlueprintCreatedData, ) +from .agent_blueprint_created_data_invocable_as import ( + AgentBlueprintCreatedDataInvocableAs as AgentBlueprintCreatedDataInvocableAs, +) from .agent_blueprint_created_data_invocable_by import ( AgentBlueprintCreatedDataInvocableBy as AgentBlueprintCreatedDataInvocableBy, ) @@ -37,29 +43,39 @@ from .agent_blueprint_deleted_data import ( AgentBlueprintDeletedData as AgentBlueprintDeletedData, ) +from .agent_blueprint_invocable_as_instance_types import ( + AgentBlueprintInvocableAsInstanceTypes as AgentBlueprintInvocableAsInstanceTypes, +) from .agent_blueprint_updated import AgentBlueprintUpdated as AgentBlueprintUpdated from .agent_blueprint_updated_data import ( AgentBlueprintUpdatedData as AgentBlueprintUpdatedData, ) +from .agent_blueprint_updated_data_invocable_as import ( + AgentBlueprintUpdatedDataInvocableAs as AgentBlueprintUpdatedDataInvocableAs, +) from .agent_blueprint_updated_data_invocable_by import ( AgentBlueprintUpdatedDataInvocableBy as AgentBlueprintUpdatedDataInvocableBy, ) from .agent_blueprint_updated_data_session_setting import ( AgentBlueprintUpdatedDataSessionSetting as AgentBlueprintUpdatedDataSessionSetting, ) +from .agent_blueprints_create_request_invocable_as_instance_types import ( + AgentBlueprintsCreateRequestInvocableAsInstanceTypes as AgentBlueprintsCreateRequestInvocableAsInstanceTypes, +) from .agent_blueprints_token_mint_token_request_type import ( AgentBlueprintsTokenMintTokenRequestType as AgentBlueprintsTokenMintTokenRequestType, ) +from .agent_blueprints_update_request_invocable_as_instance_types import ( + AgentBlueprintsUpdateRequestInvocableAsInstanceTypes as AgentBlueprintsUpdateRequestInvocableAsInstanceTypes, +) from .agent_instance_created import AgentInstanceCreated as AgentInstanceCreated from .agent_instance_created_data import ( AgentInstanceCreatedData as AgentInstanceCreatedData, ) -from .agent_instance_created_data_type import * from .agent_instance_deleted import AgentInstanceDeleted as AgentInstanceDeleted from .agent_instance_deleted_data import ( AgentInstanceDeletedData as AgentInstanceDeletedData, ) -from .agent_instance_deleted_data_type import * from .agent_instance_session_created import ( AgentInstanceSessionCreated as AgentInstanceSessionCreated, ) @@ -100,9 +116,6 @@ from .agent_registration_created_data_agent_identity import ( AgentRegistrationCreatedDataAgentIdentity as AgentRegistrationCreatedDataAgentIdentity, ) -from .agent_registration_created_data_kind import * -from .agent_registration_created_data_method import * -from .agent_registration_created_data_status import * from .agent_registration_credential_issued import ( AgentRegistrationCredentialIssued as AgentRegistrationCredentialIssued, ) @@ -299,7 +312,6 @@ from .authentication_radar_risk_detected_data import ( AuthenticationRadarRiskDetectedData as AuthenticationRadarRiskDetectedData, ) -from .authentication_radar_risk_detected_data_action import * from .authentication_reauthentication_succeeded import ( AuthenticationReauthenticationSucceeded as AuthenticationReauthenticationSucceeded, ) @@ -369,35 +381,41 @@ from .connected_account_connection_role import ( ConnectedAccountConnectionRole as ConnectedAccountConnectionRole, ) -from .connected_account_input import * from .connected_account_input_state import ( ConnectedAccountInputState as ConnectedAccountInputState, ) from .connected_account_state import ConnectedAccountState as ConnectedAccountState +from .connected_account_write_response import ( + ConnectedAccountWriteResponse as ConnectedAccountWriteResponse, +) +from .connected_account_write_response_auth_method import ( + ConnectedAccountWriteResponseAuthMethod as ConnectedAccountWriteResponseAuthMethod, +) +from .connected_account_write_response_connection_role import ( + ConnectedAccountWriteResponseConnectionRole as ConnectedAccountWriteResponseConnectionRole, +) +from .connected_account_write_response_state import ( + ConnectedAccountWriteResponseState as ConnectedAccountWriteResponseState, +) +from .connected_account_write_response_warning import ( + ConnectedAccountWriteResponseWarning as ConnectedAccountWriteResponseWarning, +) from .connection_activated import ConnectionActivated as ConnectionActivated from .connection_activated_data import ( ConnectionActivatedData as ConnectionActivatedData, ) -from .connection_activated_data_connection_type import * from .connection_activated_data_domain import ( ConnectionActivatedDataDomain as ConnectionActivatedDataDomain, ) -from .connection_activated_data_state import * -from .connection_activated_data_status import * from .connection_deactivated import ConnectionDeactivated as ConnectionDeactivated from .connection_deactivated_data import ( ConnectionDeactivatedData as ConnectionDeactivatedData, ) -from .connection_deactivated_data_connection_type import * from .connection_deactivated_data_domain import ( ConnectionDeactivatedDataDomain as ConnectionDeactivatedDataDomain, ) -from .connection_deactivated_data_state import * -from .connection_deactivated_data_status import * from .connection_deleted import ConnectionDeleted as ConnectionDeleted from .connection_deleted_data import ConnectionDeletedData as ConnectionDeletedData -from .connection_deleted_data_connection_type import * -from .connection_deleted_data_state import * from .connection_saml_certificate_renewal_required import ( ConnectionSAMLCertificateRenewalRequired as ConnectionSAMLCertificateRenewalRequired, ) @@ -407,7 +425,6 @@ from .connection_saml_certificate_renewal_required_data_certificate import ( ConnectionSAMLCertificateRenewalRequiredDataCertificate as ConnectionSAMLCertificateRenewalRequiredDataCertificate, ) -from .connection_saml_certificate_renewal_required_data_certificate_certificate_type import * from .connection_saml_certificate_renewal_required_data_connection import ( ConnectionSAMLCertificateRenewalRequiredDataConnection as ConnectionSAMLCertificateRenewalRequiredDataConnection, ) @@ -420,13 +437,15 @@ from .connection_saml_certificate_renewed_data_certificate import ( ConnectionSAMLCertificateRenewedDataCertificate as ConnectionSAMLCertificateRenewedDataCertificate, ) -from .connection_saml_certificate_renewed_data_certificate_certificate_type import * from .connection_saml_certificate_renewed_data_connection import ( ConnectionSAMLCertificateRenewedDataConnection as ConnectionSAMLCertificateRenewedDataConnection, ) from .connection_state import ConnectionState as ConnectionState from .connection_status import ConnectionStatus as ConnectionStatus from .connection_type import ConnectionType as ConnectionType +from .create_connected_account_state import ( + CreateConnectedAccountState as CreateConnectedAccountState, +) from .create_connection_oidc_options_id_token_signature_algorithm import ( CreateConnectionOIDCOptionsIdTokenSignatureAlgorithm as CreateConnectionOIDCOptionsIdTokenSignatureAlgorithm, ) @@ -439,6 +458,9 @@ from .create_data_integration_ownership import ( CreateDataIntegrationOwnership as CreateDataIntegrationOwnership, ) +from .create_organization_connected_account_state import ( + CreateOrganizationConnectedAccountState as CreateOrganizationConnectedAccountState, +) from .create_user_invite_options_locale import ( CreateUserInviteOptionsLocale as CreateUserInviteOptionsLocale, ) @@ -482,6 +504,12 @@ DataIntegrationOwnership as DataIntegrationOwnership, ) from .data_integration_state import DataIntegrationState as DataIntegrationState +from .data_integrations_create_api_key_connection_request_connection_owner import ( + DataIntegrationsCreateApiKeyConnectionRequestConnectionOwner as DataIntegrationsCreateApiKeyConnectionRequestConnectionOwner, +) +from .data_integrations_create_client_credentials_connection_request_connection_owner import ( + DataIntegrationsCreateClientCredentialsConnectionRequestConnectionOwner as DataIntegrationsCreateClientCredentialsConnectionRequestConnectionOwner, +) from .data_integrations_get_data_integration_authorize_url_request_connection_owner import ( DataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner as DataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner, ) @@ -515,9 +543,15 @@ from .data_integrations_list_response_data_ownership import ( DataIntegrationsListResponseDataOwnership as DataIntegrationsListResponseDataOwnership, ) +from .data_integrations_upsert_api_key_request_2_connection_owner import ( + DataIntegrationsUpsertApiKeyRequest2ConnectionOwner as DataIntegrationsUpsertApiKeyRequest2ConnectionOwner, +) from .data_integrations_upsert_api_key_request_connection_owner import ( DataIntegrationsUpsertApiKeyRequestConnectionOwner as DataIntegrationsUpsertApiKeyRequestConnectionOwner, ) +from .data_integrations_upsert_client_credentials_request_2_connection_owner import ( + DataIntegrationsUpsertClientCredentialsRequest2ConnectionOwner as DataIntegrationsUpsertClientCredentialsRequest2ConnectionOwner, +) from .data_integrations_upsert_client_credentials_request_connection_owner import ( DataIntegrationsUpsertClientCredentialsRequestConnectionOwner as DataIntegrationsUpsertClientCredentialsRequestConnectionOwner, ) @@ -526,10 +560,12 @@ ) from .directory_group import DirectoryGroup as DirectoryGroup from .directory_state import DirectoryState as DirectoryState +from .directory_sync_rate_limit_error import ( + DirectorySyncRateLimitError as DirectorySyncRateLimitError, +) from .directory_type import DirectoryType as DirectoryType from .directory_user import DirectoryUser as DirectoryUser from .directory_user_email import DirectoryUserEmail as DirectoryUserEmail -from .directory_user_state import * from .directory_user_with_groups_state import ( DirectoryUserWithGroupsState as DirectoryUserWithGroupsState, ) @@ -538,12 +574,8 @@ from .dsync_activated_data_domain import ( DsyncActivatedDataDomain as DsyncActivatedDataDomain, ) -from .dsync_activated_data_state import * -from .dsync_activated_data_type import * from .dsync_deleted import DsyncDeleted as DsyncDeleted from .dsync_deleted_data import DsyncDeletedData as DsyncDeletedData -from .dsync_deleted_data_state import * -from .dsync_deleted_data_type import * from .dsync_group_created import DsyncGroupCreated as DsyncGroupCreated from .dsync_group_deleted import DsyncGroupDeleted as DsyncGroupDeleted from .dsync_group_updated import DsyncGroupUpdated as DsyncGroupUpdated @@ -567,7 +599,6 @@ from .dsync_user_updated_data_email import ( DsyncUserUpdatedDataEmail as DsyncUserUpdatedDataEmail, ) -from .dsync_user_updated_data_state import * from .email_verification_created import ( EmailVerificationCreated as EmailVerificationCreated, ) @@ -577,7 +608,6 @@ from .error_response import ErrorResponse as ErrorResponse from .event_context import EventContext as EventContext from .event_context_actor import EventContextActor as EventContextActor -from .event_context_actor_source import * from .event_context_google_analytics_session import ( EventContextGoogleAnalyticsSession as EventContextGoogleAnalyticsSession, ) @@ -589,7 +619,6 @@ from .flag_created_context_actor import ( FlagCreatedContextActor as FlagCreatedContextActor, ) -from .flag_created_context_actor_source import * from .flag_created_data import FlagCreatedData as FlagCreatedData from .flag_created_data_owner import FlagCreatedDataOwner as FlagCreatedDataOwner from .flag_deleted import FlagDeleted as FlagDeleted @@ -597,17 +626,14 @@ from .flag_deleted_context_actor import ( FlagDeletedContextActor as FlagDeletedContextActor, ) -from .flag_deleted_context_actor_source import * from .flag_deleted_data import FlagDeletedData as FlagDeletedData from .flag_deleted_data_owner import FlagDeletedDataOwner as FlagDeletedDataOwner from .flag_owner import FlagOwner as FlagOwner from .flag_rule_updated import FlagRuleUpdated as FlagRuleUpdated from .flag_rule_updated_context import FlagRuleUpdatedContext as FlagRuleUpdatedContext -from .flag_rule_updated_context_access_type import * from .flag_rule_updated_context_actor import ( FlagRuleUpdatedContextActor as FlagRuleUpdatedContextActor, ) -from .flag_rule_updated_context_actor_source import * from .flag_rule_updated_context_configured_target import ( FlagRuleUpdatedContextConfiguredTarget as FlagRuleUpdatedContextConfiguredTarget, ) @@ -623,7 +649,6 @@ from .flag_rule_updated_context_previous_attribute_context import ( FlagRuleUpdatedContextPreviousAttributeContext as FlagRuleUpdatedContextPreviousAttributeContext, ) -from .flag_rule_updated_context_previous_attribute_context_access_type import * from .flag_rule_updated_context_previous_attribute_context_configured_target import ( FlagRuleUpdatedContextPreviousAttributeContextConfiguredTarget as FlagRuleUpdatedContextPreviousAttributeContextConfiguredTarget, ) @@ -645,7 +670,6 @@ from .flag_updated_context_actor import ( FlagUpdatedContextActor as FlagUpdatedContextActor, ) -from .flag_updated_context_actor_source import * from .flag_updated_context_previous_attribute import ( FlagUpdatedContextPreviousAttribute as FlagUpdatedContextPreviousAttribute, ) @@ -665,21 +689,20 @@ from .group_updated import GroupUpdated as GroupUpdated from .invitation_accepted import InvitationAccepted as InvitationAccepted from .invitation_accepted_data import InvitationAcceptedData as InvitationAcceptedData -from .invitation_accepted_data_state import * from .invitation_created import InvitationCreated as InvitationCreated from .invitation_created_data import InvitationCreatedData as InvitationCreatedData -from .invitation_created_data_state import * from .invitation_resent import InvitationResent as InvitationResent from .invitation_resent_data import InvitationResentData as InvitationResentData -from .invitation_resent_data_state import * from .invitation_revoked import InvitationRevoked as InvitationRevoked from .invitation_revoked_data import InvitationRevokedData as InvitationRevokedData -from .invitation_revoked_data_state import * from .invitation_state import InvitationState as InvitationState from .invite_it_contact_intents import InviteItContactIntents as InviteItContactIntents from .list_metadata import ListMetadata as ListMetadata from .magic_auth_created import MagicAuthCreated as MagicAuthCreated from .magic_auth_created_data import MagicAuthCreatedData as MagicAuthCreatedData +from .organization_connected_account_state import ( + OrganizationConnectedAccountState as OrganizationConnectedAccountState, +) from .organization_created import OrganizationCreated as OrganizationCreated from .organization_created_data import ( OrganizationCreatedData as OrganizationCreatedData, @@ -687,8 +710,6 @@ from .organization_created_data_domain import ( OrganizationCreatedDataDomain as OrganizationCreatedDataDomain, ) -from .organization_created_data_domain_state import * -from .organization_created_data_domain_verification_strategy import * from .organization_deleted import OrganizationDeleted as OrganizationDeleted from .organization_deleted_data import ( OrganizationDeletedData as OrganizationDeletedData, @@ -696,8 +717,6 @@ from .organization_deleted_data_domain import ( OrganizationDeletedDataDomain as OrganizationDeletedDataDomain, ) -from .organization_deleted_data_domain_state import * -from .organization_deleted_data_domain_verification_strategy import * from .organization_domain import OrganizationDomain as OrganizationDomain from .organization_domain_created import ( OrganizationDomainCreated as OrganizationDomainCreated, @@ -705,8 +724,6 @@ from .organization_domain_created_data import ( OrganizationDomainCreatedData as OrganizationDomainCreatedData, ) -from .organization_domain_created_data_state import * -from .organization_domain_created_data_verification_strategy import * from .organization_domain_data_state import ( OrganizationDomainDataState as OrganizationDomainDataState, ) @@ -716,8 +733,6 @@ from .organization_domain_deleted_data import ( OrganizationDomainDeletedData as OrganizationDomainDeletedData, ) -from .organization_domain_deleted_data_state import * -from .organization_domain_deleted_data_verification_strategy import * from .organization_domain_state import ( OrganizationDomainState as OrganizationDomainState, ) @@ -727,8 +742,6 @@ from .organization_domain_updated_data import ( OrganizationDomainUpdatedData as OrganizationDomainUpdatedData, ) -from .organization_domain_updated_data_state import * -from .organization_domain_updated_data_verification_strategy import * from .organization_domain_verification_failed import ( OrganizationDomainVerificationFailed as OrganizationDomainVerificationFailed, ) @@ -738,9 +751,6 @@ from .organization_domain_verification_failed_data_organization_domain import ( OrganizationDomainVerificationFailedDataOrganizationDomain as OrganizationDomainVerificationFailedDataOrganizationDomain, ) -from .organization_domain_verification_failed_data_organization_domain_state import * -from .organization_domain_verification_failed_data_organization_domain_verification_strategy import * -from .organization_domain_verification_failed_data_reason import * from .organization_domain_verification_strategy import ( OrganizationDomainVerificationStrategy as OrganizationDomainVerificationStrategy, ) @@ -750,22 +760,18 @@ from .organization_domain_verified_data import ( OrganizationDomainVerifiedData as OrganizationDomainVerifiedData, ) -from .organization_domain_verified_data_state import * -from .organization_domain_verified_data_verification_strategy import * from .organization_membership_created import ( OrganizationMembershipCreated as OrganizationMembershipCreated, ) from .organization_membership_created_data import ( OrganizationMembershipCreatedData as OrganizationMembershipCreatedData, ) -from .organization_membership_created_data_status import * from .organization_membership_deleted import ( OrganizationMembershipDeleted as OrganizationMembershipDeleted, ) from .organization_membership_deleted_data import ( OrganizationMembershipDeletedData as OrganizationMembershipDeletedData, ) -from .organization_membership_deleted_data_status import * from .organization_membership_status import ( OrganizationMembershipStatus as OrganizationMembershipStatus, ) @@ -775,7 +781,6 @@ from .organization_membership_updated_data import ( OrganizationMembershipUpdatedData as OrganizationMembershipUpdatedData, ) -from .organization_membership_updated_data_status import * from .organization_role_created import ( OrganizationRoleCreated as OrganizationRoleCreated, ) @@ -801,8 +806,6 @@ from .organization_updated_data_domain import ( OrganizationUpdatedDataDomain as OrganizationUpdatedDataDomain, ) -from .organization_updated_data_domain_state import * -from .organization_updated_data_domain_verification_strategy import * from .pagination_order import PaginationOrder as PaginationOrder from .password_reset_created import PasswordResetCreated as PasswordResetCreated from .password_reset_created_data import ( @@ -825,9 +828,6 @@ from .permission_updated import PermissionUpdated as PermissionUpdated from .permission_updated_data import PermissionUpdatedData as PermissionUpdatedData from .pipe_connected_account import PipeConnectedAccount as PipeConnectedAccount -from .pipe_connected_account_auth_method import * -from .pipe_connected_account_connection_role import * -from .pipe_connected_account_state import * from .pipes_account_connection_add_failed import ( PipesAccountConnectionAddFailed as PipesAccountConnectionAddFailed, ) @@ -884,6 +884,15 @@ from .radar_standalone_response_verdict import ( RadarStandaloneResponseVerdict as RadarStandaloneResponseVerdict, ) +from .reauthorize_data_integrations_get_data_integration_authorize_url_request_connection_owner import ( + ReauthorizeDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner as ReauthorizeDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner, +) +from .reauthorize_data_integrations_upsert_api_key_request_connection_owner import ( + ReauthorizeDataIntegrationsUpsertApiKeyRequestConnectionOwner as ReauthorizeDataIntegrationsUpsertApiKeyRequestConnectionOwner, +) +from .reauthorize_data_integrations_upsert_client_credentials_request_connection_owner import ( + ReauthorizeDataIntegrationsUpsertClientCredentialsRequestConnectionOwner as ReauthorizeDataIntegrationsUpsertClientCredentialsRequestConnectionOwner, +) from .resend_user_invite_options_locale import ( ResendUserInviteOptionsLocale as ResendUserInviteOptionsLocale, ) @@ -893,24 +902,20 @@ from .resource_export_completed_data import ( ResourceExportCompletedData as ResourceExportCompletedData, ) -from .resource_export_completed_data_resource_type import * from .resource_export_created import ResourceExportCreated as ResourceExportCreated from .resource_export_created_data import ( ResourceExportCreatedData as ResourceExportCreatedData, ) -from .resource_export_created_data_resource_type import * from .resource_export_downloaded import ( ResourceExportDownloaded as ResourceExportDownloaded, ) from .resource_export_downloaded_data import ( ResourceExportDownloadedData as ResourceExportDownloadedData, ) -from .resource_export_downloaded_data_resource_type import * from .resource_export_failed import ResourceExportFailed as ResourceExportFailed from .resource_export_failed_data import ( ResourceExportFailedData as ResourceExportFailedData, ) -from .resource_export_failed_data_resource_type import * from .role_created import RoleCreated as RoleCreated from .role_created_data import RoleCreatedData as RoleCreatedData from .role_deleted import RoleDeleted as RoleDeleted @@ -920,18 +925,14 @@ from .role_updated_data import RoleUpdatedData as RoleUpdatedData from .session_created import SessionCreated as SessionCreated from .session_created_data import SessionCreatedData as SessionCreatedData -from .session_created_data_auth_method import * from .session_created_data_impersonator import ( SessionCreatedDataImpersonator as SessionCreatedDataImpersonator, ) -from .session_created_data_status import * from .session_revoked import SessionRevoked as SessionRevoked from .session_revoked_data import SessionRevokedData as SessionRevokedData -from .session_revoked_data_auth_method import * from .session_revoked_data_impersonator import ( SessionRevokedDataImpersonator as SessionRevokedDataImpersonator, ) -from .session_revoked_data_status import * from .slim_role import SlimRole as SlimRole from .team_production_state import TeamProductionState as TeamProductionState from .token_query_grant_type import TokenQueryGrantType as TokenQueryGrantType @@ -992,7 +993,6 @@ from .vault_byok_key_deleted_data import ( VaultByokKeyDeletedData as VaultByokKeyDeletedData, ) -from .vault_byok_key_provider import * from .vault_byok_key_verification_completed import ( VaultByokKeyVerificationCompleted as VaultByokKeyVerificationCompleted, ) @@ -1001,40 +1001,31 @@ ) from .vault_data_created import VaultDataCreated as VaultDataCreated from .vault_data_created_data import VaultDataCreatedData as VaultDataCreatedData -from .vault_data_created_data_actor_source import * from .vault_data_deleted import VaultDataDeleted as VaultDataDeleted from .vault_data_deleted_data import VaultDataDeletedData as VaultDataDeletedData -from .vault_data_deleted_data_actor_source import * from .vault_data_read import VaultDataRead as VaultDataRead from .vault_data_read_data import VaultDataReadData as VaultDataReadData -from .vault_data_read_data_actor_source import * from .vault_data_updated import VaultDataUpdated as VaultDataUpdated from .vault_data_updated_data import VaultDataUpdatedData as VaultDataUpdatedData -from .vault_data_updated_data_actor_source import * from .vault_dek_decrypted import VaultDekDecrypted as VaultDekDecrypted from .vault_dek_decrypted_data import VaultDekDecryptedData as VaultDekDecryptedData -from .vault_dek_decrypted_data_actor_source import * from .vault_dek_read import VaultDekRead as VaultDekRead from .vault_dek_read_data import VaultDekReadData as VaultDekReadData -from .vault_dek_read_data_actor_source import * +from .vault_dek_rekeyed import VaultDekRekeyed as VaultDekRekeyed +from .vault_dek_rekeyed_data import VaultDekRekeyedData as VaultDekRekeyedData from .vault_kek_created import VaultKekCreated as VaultKekCreated from .vault_kek_created_data import VaultKekCreatedData as VaultKekCreatedData -from .vault_kek_created_data_actor_source import * from .vault_kek_deleted import VaultKekDeleted as VaultKekDeleted from .vault_kek_deleted_data import VaultKekDeletedData as VaultKekDeletedData -from .vault_kek_deleted_data_actor_source import * from .vault_metadata_read import VaultMetadataRead as VaultMetadataRead from .vault_metadata_read_data import VaultMetadataReadData as VaultMetadataReadData -from .vault_metadata_read_data_actor_source import * from .vault_names_listed import VaultNamesListed as VaultNamesListed from .vault_names_listed_data import VaultNamesListedData as VaultNamesListedData -from .vault_names_listed_data_actor_source import * from .waitlist_entry_state import WaitlistEntryState as WaitlistEntryState from .waitlist_user import WaitlistUser as WaitlistUser from .waitlist_user_approved import WaitlistUserApproved as WaitlistUserApproved from .waitlist_user_created import WaitlistUserCreated as WaitlistUserCreated from .waitlist_user_denied import WaitlistUserDenied as WaitlistUserDenied -from .waitlist_user_state import * from .webhook_endpoint_status import WebhookEndpointStatus as WebhookEndpointStatus from .widget_session_token_scopes import ( WidgetSessionTokenScopes as WidgetSessionTokenScopes, diff --git a/src/workos/common/models/agent_blueprint_invocable_as_instance_types.py b/src/workos/common/models/agent_blueprint_invocable_as_instance_types.py new file mode 100644 index 00000000..c9c0398e --- /dev/null +++ b/src/workos/common/models/agent_blueprint_invocable_as_instance_types.py @@ -0,0 +1,12 @@ +# This file is auto-generated by oagen. Do not edit. + +from typing import TypeAlias + +from .agent_blueprint_created_data_invocable_as_instance_types import ( + AgentBlueprintCreatedDataInvocableAsInstanceTypes, +) + +AgentBlueprintInvocableAsInstanceTypes: TypeAlias = ( + AgentBlueprintCreatedDataInvocableAsInstanceTypes +) +__all__ = ["AgentBlueprintInvocableAsInstanceTypes"] diff --git a/src/workos/common/models/agent_blueprint_updated_data_invocable_as.py b/src/workos/common/models/agent_blueprint_updated_data_invocable_as.py new file mode 100644 index 00000000..bdbd4160 --- /dev/null +++ b/src/workos/common/models/agent_blueprint_updated_data_invocable_as.py @@ -0,0 +1,9 @@ +# This file is auto-generated by oagen. Do not edit. + +from typing import TypeAlias + +from .agent_blueprint_created_data_invocable_as import ( + AgentBlueprintCreatedDataInvocableAs, +) + +AgentBlueprintUpdatedDataInvocableAs: TypeAlias = AgentBlueprintCreatedDataInvocableAs diff --git a/src/workos/common/models/agent_blueprint_updated_data_invocable_as_instance_types.py b/src/workos/common/models/agent_blueprint_updated_data_invocable_as_instance_types.py new file mode 100644 index 00000000..8ae8f44b --- /dev/null +++ b/src/workos/common/models/agent_blueprint_updated_data_invocable_as_instance_types.py @@ -0,0 +1,12 @@ +# This file is auto-generated by oagen. Do not edit. + +from typing import TypeAlias + +from .agent_blueprint_created_data_invocable_as_instance_types import ( + AgentBlueprintCreatedDataInvocableAsInstanceTypes, +) + +AgentBlueprintUpdatedDataInvocableAsInstanceTypes: TypeAlias = ( + AgentBlueprintCreatedDataInvocableAsInstanceTypes +) +__all__ = ["AgentBlueprintUpdatedDataInvocableAsInstanceTypes"] diff --git a/src/workos/common/models/agent_blueprints_update_request_invocable_as_instance_types.py b/src/workos/common/models/agent_blueprints_update_request_invocable_as_instance_types.py new file mode 100644 index 00000000..01dc170a --- /dev/null +++ b/src/workos/common/models/agent_blueprints_update_request_invocable_as_instance_types.py @@ -0,0 +1,12 @@ +# This file is auto-generated by oagen. Do not edit. + +from typing import TypeAlias + +from .agent_blueprint_created_data_invocable_as_instance_types import ( + AgentBlueprintCreatedDataInvocableAsInstanceTypes, +) + +AgentBlueprintsUpdateRequestInvocableAsInstanceTypes: TypeAlias = ( + AgentBlueprintCreatedDataInvocableAsInstanceTypes +) +__all__ = ["AgentBlueprintsUpdateRequestInvocableAsInstanceTypes"] diff --git a/src/workos/common/models/agent_instance_deleted_data_type.py b/src/workos/common/models/agent_instance_deleted_data_type.py index 77860aef..914f500a 100644 --- a/src/workos/common/models/agent_instance_deleted_data_type.py +++ b/src/workos/common/models/agent_instance_deleted_data_type.py @@ -2,7 +2,11 @@ from typing import TypeAlias -from .agent_instance_created_data_type import AgentInstanceCreatedDataType +from .agent_blueprint_created_data_invocable_as_instance_types import ( + AgentBlueprintCreatedDataInvocableAsInstanceTypes, +) -AgentInstanceDeletedDataType: TypeAlias = AgentInstanceCreatedDataType +AgentInstanceDeletedDataType: TypeAlias = ( + AgentBlueprintCreatedDataInvocableAsInstanceTypes +) __all__ = ["AgentInstanceDeletedDataType"] diff --git a/src/workos/common/models/agent_instance_type.py b/src/workos/common/models/agent_instance_type.py index ffb91179..4a0c3c2a 100644 --- a/src/workos/common/models/agent_instance_type.py +++ b/src/workos/common/models/agent_instance_type.py @@ -2,7 +2,9 @@ from typing import TypeAlias -from .agent_instance_created_data_type import AgentInstanceCreatedDataType +from .agent_blueprint_created_data_invocable_as_instance_types import ( + AgentBlueprintCreatedDataInvocableAsInstanceTypes, +) -AgentInstanceType: TypeAlias = AgentInstanceCreatedDataType +AgentInstanceType: TypeAlias = AgentBlueprintCreatedDataInvocableAsInstanceTypes __all__ = ["AgentInstanceType"] diff --git a/src/workos/common/models/connected_account_input.py b/src/workos/common/models/connected_account_input.py deleted file mode 100644 index fe664f5b..00000000 --- a/src/workos/common/models/connected_account_input.py +++ /dev/null @@ -1,63 +0,0 @@ -# This file is auto-generated by oagen. Do not edit. - -from __future__ import annotations - -from dataclasses import dataclass -from datetime import datetime -from enum import Enum -from typing import Any - -from workos._types import _format_datetime, _parse_datetime, _raise_deserialize_error - -from .connected_account_input_state import ConnectedAccountInputState - - -@dataclass(slots=True) -class ConnectedAccountInput: - """Connected Account Input model.""" - - access_token: str | None = None - """The OAuth access token for the connected account.""" - refresh_token: str | None = None - """The OAuth refresh token for the connected account.""" - expires_at: datetime | None = None - """The ISO-8601 timestamp when the access token expires. Required when `access_token` is provided for tokens that expire.""" - scopes: list[str] | None = None - """The OAuth scopes granted for this connection.""" - state: ConnectedAccountInputState | None = None - """Explicitly set the state of the connected account. When omitted, the state is derived from the token combination provided.""" - - @classmethod - def from_dict(cls, data: dict[str, Any]) -> ConnectedAccountInput: - """Deserialize from a dictionary.""" - try: - return cls( - access_token=data.get("access_token"), - refresh_token=data.get("refresh_token"), - expires_at=_parse_datetime(_v_expires_at) - if (_v_expires_at := data.get("expires_at")) is not None - else None, - scopes=data.get("scopes"), - state=ConnectedAccountInputState(_v_state) - if (_v_state := data.get("state")) is not None - else None, - ) - except (KeyError, ValueError) as e: - _raise_deserialize_error("ConnectedAccountInput", e) - - def to_dict(self) -> dict[str, Any]: - """Serialize to a dictionary.""" - result: dict[str, Any] = {} - if self.access_token is not None: - result["access_token"] = self.access_token - if self.refresh_token is not None: - result["refresh_token"] = self.refresh_token - if self.expires_at is not None: - result["expires_at"] = _format_datetime(self.expires_at) - if self.scopes is not None: - result["scopes"] = self.scopes - if self.state is not None: - result["state"] = ( - self.state.value if isinstance(self.state, Enum) else self.state - ) - return result diff --git a/src/workos/common/models/data_integrations_upsert_api_key_request_2_connection_owner.py b/src/workos/common/models/data_integrations_upsert_api_key_request_2_connection_owner.py new file mode 100644 index 00000000..130ead8c --- /dev/null +++ b/src/workos/common/models/data_integrations_upsert_api_key_request_2_connection_owner.py @@ -0,0 +1,12 @@ +# This file is auto-generated by oagen. Do not edit. + +from typing import TypeAlias + +from .add_data_integrations_get_data_integration_authorize_url_request_connection_owner import ( + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner, +) + +DataIntegrationsUpsertApiKeyRequest2ConnectionOwner: TypeAlias = ( + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner +) +__all__ = ["DataIntegrationsUpsertApiKeyRequest2ConnectionOwner"] diff --git a/src/workos/common/models/data_integrations_upsert_api_key_request_connection_owner.py b/src/workos/common/models/data_integrations_upsert_api_key_request_connection_owner.py index 3a5630ac..34d68d3c 100644 --- a/src/workos/common/models/data_integrations_upsert_api_key_request_connection_owner.py +++ b/src/workos/common/models/data_integrations_upsert_api_key_request_connection_owner.py @@ -2,9 +2,11 @@ from typing import TypeAlias -from .create_data_integration_ownership import CreateDataIntegrationOwnership +from .add_data_integrations_get_data_integration_authorize_url_request_connection_owner import ( + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner, +) DataIntegrationsUpsertApiKeyRequestConnectionOwner: TypeAlias = ( - CreateDataIntegrationOwnership + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner ) __all__ = ["DataIntegrationsUpsertApiKeyRequestConnectionOwner"] diff --git a/src/workos/common/models/directory_sync_rate_limit_error.py b/src/workos/common/models/directory_sync_rate_limit_error.py new file mode 100644 index 00000000..1b610551 --- /dev/null +++ b/src/workos/common/models/directory_sync_rate_limit_error.py @@ -0,0 +1,40 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, Literal + +from workos._types import _raise_deserialize_error + + +@dataclass(slots=True) +class DirectorySyncRateLimitError: + """Directory Sync Rate Limit Error model.""" + + code: Literal["directory_sync_rate_limited"] + """The error code identifying the type of error.""" + message: str + """A human-readable description of the error.""" + retry_after_seconds: int + """The number of seconds to wait before requesting another manual sync of this directory.""" + + @classmethod + def from_dict(cls, data: dict[str, Any]) -> DirectorySyncRateLimitError: + """Deserialize from a dictionary.""" + try: + return cls( + code=data.get("code", "directory_sync_rate_limited"), + message=data["message"], + retry_after_seconds=data["retry_after_seconds"], + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error("DirectorySyncRateLimitError", e) + + def to_dict(self) -> dict[str, Any]: + """Serialize to a dictionary.""" + result: dict[str, Any] = {} + result["code"] = self.code + result["message"] = self.message + result["retry_after_seconds"] = self.retry_after_seconds + return result diff --git a/src/workos/common/models/reauthorize_data_integrations_upsert_api_key_request_connection_owner.py b/src/workos/common/models/reauthorize_data_integrations_upsert_api_key_request_connection_owner.py new file mode 100644 index 00000000..35a14918 --- /dev/null +++ b/src/workos/common/models/reauthorize_data_integrations_upsert_api_key_request_connection_owner.py @@ -0,0 +1,12 @@ +# This file is auto-generated by oagen. Do not edit. + +from typing import TypeAlias + +from .add_data_integrations_get_data_integration_authorize_url_request_connection_owner import ( + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner, +) + +ReauthorizeDataIntegrationsUpsertApiKeyRequestConnectionOwner: TypeAlias = ( + AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner +) +__all__ = ["ReauthorizeDataIntegrationsUpsertApiKeyRequestConnectionOwner"] diff --git a/src/workos/common/models/vault_dek_rekeyed.py b/src/workos/common/models/vault_dek_rekeyed.py new file mode 100644 index 00000000..01fed539 --- /dev/null +++ b/src/workos/common/models/vault_dek_rekeyed.py @@ -0,0 +1,57 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from datetime import datetime +from typing import Any, Literal, cast + +from workos._types import _format_datetime, _parse_datetime, _raise_deserialize_error + +from .event_context import EventContext +from .vault_dek_rekeyed_data import VaultDekRekeyedData + + +@dataclass(slots=True) +class VaultDekRekeyed: + """Vault Dek Rekeyed model.""" + + object: Literal["event"] + """Distinguishes the Event object.""" + id: str + """Unique identifier for the event.""" + event: Literal["vault.dek.rekeyed"] + data: VaultDekRekeyedData + """The event payload.""" + created_at: datetime + """An ISO 8601 timestamp.""" + context: EventContext | None = None + + @classmethod + def from_dict(cls, data: dict[str, Any]) -> VaultDekRekeyed: + """Deserialize from a dictionary.""" + try: + return cls( + object=data.get("object", "event"), + id=data["id"], + event=data.get("event", "vault.dek.rekeyed"), + data=VaultDekRekeyedData.from_dict(cast(dict[str, Any], data["data"])), + created_at=_parse_datetime(data["created_at"]), + context=EventContext.from_dict(cast(dict[str, Any], _v_context)) + if (_v_context := data.get("context")) is not None + else None, + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error("VaultDekRekeyed", e) + + def to_dict(self) -> dict[str, Any]: + """Serialize to a dictionary.""" + result: dict[str, Any] = {} + result["object"] = self.object + result["id"] = self.id + result["event"] = self.event + result["data"] = self.data.to_dict() + result["created_at"] = _format_datetime(self.created_at) + if self.context is not None: + result["context"] = self.context.to_dict() + return result diff --git a/src/workos/common/models/vault_dek_rekeyed_data.py b/src/workos/common/models/vault_dek_rekeyed_data.py new file mode 100644 index 00000000..7bef1983 --- /dev/null +++ b/src/workos/common/models/vault_dek_rekeyed_data.py @@ -0,0 +1,7 @@ +# This file is auto-generated by oagen. Do not edit. + +from typing import TypeAlias + +from .vault_dek_read_data import VaultDekReadData + +VaultDekRekeyedData: TypeAlias = VaultDekReadData diff --git a/src/workos/common/models/vault_dek_rekeyed_data_actor_source.py b/src/workos/common/models/vault_dek_rekeyed_data_actor_source.py new file mode 100644 index 00000000..77d57a59 --- /dev/null +++ b/src/workos/common/models/vault_dek_rekeyed_data_actor_source.py @@ -0,0 +1,8 @@ +# This file is auto-generated by oagen. Do not edit. + +from typing import TypeAlias + +from .vault_data_created_data_actor_source import VaultDataCreatedDataActorSource + +VaultDekRekeyedDataActorSource: TypeAlias = VaultDataCreatedDataActorSource +__all__ = ["VaultDekRekeyedDataActorSource"] diff --git a/src/workos/directory_sync/_resource.py b/src/workos/directory_sync/_resource.py index 0a7bdd45..ba9bad15 100644 --- a/src/workos/directory_sync/_resource.py +++ b/src/workos/directory_sync/_resource.py @@ -12,7 +12,7 @@ from .._pagination import AsyncPage, SyncPage from .._types import RequestOptions, enum_value -from .models import Directory, DirectoryUserWithGroups +from .models import Directory, DirectorySyncResponse, DirectoryUserWithGroups class DirectorySync: @@ -135,6 +135,39 @@ def delete_directory( request_options=request_options, ) + def create_directory_sync( + self, + id: str, + *, + request_options: RequestOptions | None = None, + ) -> DirectorySyncResponse: + """Sync a Directory + + Request an asynchronous sync from the directory provider. Currently supports Google Workspace directories in linked or validating state. Manual requests share a five-minute per-directory cooldown across the API, Dashboard, Admin Portal, and MCP. Acceptance means the request was queued, not that the sync has started or completed. A running sync prevents another request from being queued. + + Args: + id: Unique identifier for the Directory. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + DirectorySyncResponse + + Raises: + AuthorizationError: If the request is forbidden (403). + NotFoundError: If the resource is not found (404). + ConflictError: If a conflict occurs (409). + UnprocessableEntityError: If the request data is unprocessable (422). + RateLimitExceededError: If rate limited (429). + AuthenticationError: If the API key is invalid (401). + ServerError: If the server returns a 5xx error. + """ + return self._client.request( + method="post", + path=("directories", str(id), "sync"), + model=DirectorySyncResponse, + request_options=request_options, + ) + def list_groups( self, *, @@ -434,6 +467,39 @@ async def delete_directory( request_options=request_options, ) + async def create_directory_sync( + self, + id: str, + *, + request_options: RequestOptions | None = None, + ) -> DirectorySyncResponse: + """Sync a Directory + + Request an asynchronous sync from the directory provider. Currently supports Google Workspace directories in linked or validating state. Manual requests share a five-minute per-directory cooldown across the API, Dashboard, Admin Portal, and MCP. Acceptance means the request was queued, not that the sync has started or completed. A running sync prevents another request from being queued. + + Args: + id: Unique identifier for the Directory. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + DirectorySyncResponse + + Raises: + AuthorizationError: If the request is forbidden (403). + NotFoundError: If the resource is not found (404). + ConflictError: If a conflict occurs (409). + UnprocessableEntityError: If the request data is unprocessable (422). + RateLimitExceededError: If rate limited (429). + AuthenticationError: If the API key is invalid (401). + ServerError: If the server returns a 5xx error. + """ + return await self._client.request( + method="post", + path=("directories", str(id), "sync"), + model=DirectorySyncResponse, + request_options=request_options, + ) + async def list_groups( self, *, diff --git a/src/workos/directory_sync/models/__init__.py b/src/workos/directory_sync/models/__init__.py index f315d1b1..ecee47f3 100644 --- a/src/workos/directory_sync/models/__init__.py +++ b/src/workos/directory_sync/models/__init__.py @@ -5,6 +5,7 @@ from .directory import Directory as Directory from .directory_metadata import DirectoryMetadata as DirectoryMetadata from .directory_metadata_user import DirectoryMetadataUser as DirectoryMetadataUser +from .directory_sync_response import DirectorySyncResponse as DirectorySyncResponse from .directory_user_with_groups import ( DirectoryUserWithGroups as DirectoryUserWithGroups, ) diff --git a/src/workos/directory_sync/models/directory_sync_response.py b/src/workos/directory_sync/models/directory_sync_response.py new file mode 100644 index 00000000..1d3e3f6c --- /dev/null +++ b/src/workos/directory_sync/models/directory_sync_response.py @@ -0,0 +1,32 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, Literal + +from workos._types import _raise_deserialize_error + + +@dataclass(slots=True) +class DirectorySyncResponse: + """Directory Sync Response model.""" + + status: Literal["queued"] + """The sync request was queued for asynchronous processing. This does not indicate that the sync has started or completed.""" + + @classmethod + def from_dict(cls, data: dict[str, Any]) -> DirectorySyncResponse: + """Deserialize from a dictionary.""" + try: + return cls( + status=data.get("status", "queued"), + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error("DirectorySyncResponse", e) + + def to_dict(self) -> dict[str, Any]: + """Serialize to a dictionary.""" + result: dict[str, Any] = {} + result["status"] = self.status + return result diff --git a/src/workos/events/models/event_schema.py b/src/workos/events/models/event_schema.py index 886e0966..4f68fb86 100644 --- a/src/workos/events/models/event_schema.py +++ b/src/workos/events/models/event_schema.py @@ -194,6 +194,7 @@ from workos.common.models.vault_data_updated import VaultDataUpdated from workos.common.models.vault_dek_decrypted import VaultDekDecrypted from workos.common.models.vault_dek_read import VaultDekRead +from workos.common.models.vault_dek_rekeyed import VaultDekRekeyed from workos.common.models.vault_kek_created import VaultKekCreated from workos.common.models.vault_kek_deleted import VaultKekDeleted from workos.common.models.vault_metadata_read import VaultMetadataRead @@ -341,6 +342,7 @@ def to_dict(self) -> dict[str, Any]: VaultDataUpdated, VaultDekDecrypted, VaultDekRead, + VaultDekRekeyed, VaultKekCreated, VaultKekDeleted, VaultMetadataRead, @@ -476,6 +478,7 @@ class EventSchema: "vault.data.updated": VaultDataUpdated, "vault.dek.decrypted": VaultDekDecrypted, "vault.dek.read": VaultDekRead, + "vault.dek.rekeyed": VaultDekRekeyed, "vault.kek.created": VaultKekCreated, "vault.kek.deleted": VaultKekDeleted, "vault.metadata.read": VaultMetadataRead, diff --git a/src/workos/organization_membership/_resource.py b/src/workos/organization_membership/_resource.py index 4004faca..9e3cd979 100644 --- a/src/workos/organization_membership/_resource.py +++ b/src/workos/organization_membership/_resource.py @@ -236,6 +236,7 @@ def delete_organization_membership( Raises: NotFoundError: If the resource is not found (404). + UnprocessableEntityError: If the request data is unprocessable (422). AuthenticationError: If the API key is invalid (401). RateLimitExceededError: If rate limited (429). ServerError: If the server returns a 5xx error. @@ -571,6 +572,7 @@ async def delete_organization_membership( Raises: NotFoundError: If the resource is not found (404). + UnprocessableEntityError: If the request data is unprocessable (422). AuthenticationError: If the API key is invalid (401). RateLimitExceededError: If rate limited (429). ServerError: If the server returns a 5xx error. diff --git a/src/workos/sso/_resource.py b/src/workos/sso/_resource.py index accfa86e..68ce572a 100644 --- a/src/workos/sso/_resource.py +++ b/src/workos/sso/_resource.py @@ -804,6 +804,7 @@ def get_profile_and_token( self, *, code: str | None = None, + code_verifier: str | None = None, subject_token: str | None = None, subject_token_type: Literal["urn:ietf:params:oauth:token-type:id_token"] | None = None, @@ -816,6 +817,7 @@ def get_profile_and_token( Args: code: The authorization code received from the authorization callback. Required when `grant_type` is `authorization_code`. + code_verifier: The PKCE code verifier matching the `code_challenge` sent with the authorization request. Required when the authorization request included a `code_challenge`. Must be sent in the request body. subject_token: The OIDC ID token to exchange. Required when `grant_type` is `urn:ietf:params:oauth:grant-type:token-exchange`. Must be sent in the request body. subject_token_type: The type of the subject token. Required when `grant_type` is `urn:ietf:params:oauth:grant-type:token-exchange`. Must be sent in the request body. organization_id: The ID of the organization whose connection the subject token is validated against. Required when `grant_type` is `urn:ietf:params:oauth:grant-type:token-exchange`. Must be sent in the request body. @@ -836,6 +838,7 @@ def get_profile_and_token( k: v for k, v in { "code": code, + "code_verifier": code_verifier, "subject_token": subject_token, "subject_token_type": subject_token_type, "organization_id": organization_id, @@ -1672,6 +1675,7 @@ async def get_profile_and_token( self, *, code: str | None = None, + code_verifier: str | None = None, subject_token: str | None = None, subject_token_type: Literal["urn:ietf:params:oauth:token-type:id_token"] | None = None, @@ -1684,6 +1688,7 @@ async def get_profile_and_token( Args: code: The authorization code received from the authorization callback. Required when `grant_type` is `authorization_code`. + code_verifier: The PKCE code verifier matching the `code_challenge` sent with the authorization request. Required when the authorization request included a `code_challenge`. Must be sent in the request body. subject_token: The OIDC ID token to exchange. Required when `grant_type` is `urn:ietf:params:oauth:grant-type:token-exchange`. Must be sent in the request body. subject_token_type: The type of the subject token. Required when `grant_type` is `urn:ietf:params:oauth:grant-type:token-exchange`. Must be sent in the request body. organization_id: The ID of the organization whose connection the subject token is validated against. Required when `grant_type` is `urn:ietf:params:oauth:grant-type:token-exchange`. Must be sent in the request body. @@ -1704,6 +1709,7 @@ async def get_profile_and_token( k: v for k, v in { "code": code, + "code_verifier": code_verifier, "subject_token": subject_token, "subject_token_type": subject_token_type, "organization_id": organization_id, diff --git a/src/workos/sso/models/token_query.py b/src/workos/sso/models/token_query.py index 962ca4b1..fd39dbbf 100644 --- a/src/workos/sso/models/token_query.py +++ b/src/workos/sso/models/token_query.py @@ -22,6 +22,8 @@ class TokenQuery: """The grant type for the token request.""" code: str | None = None """The authorization code received from the authorization callback. Required when `grant_type` is `authorization_code`.""" + code_verifier: str | None = None + """The PKCE code verifier matching the `code_challenge` sent with the authorization request. Required when the authorization request included a `code_challenge`. Must be sent in the request body.""" subject_token: str | None = None """The OIDC ID token to exchange. Required when `grant_type` is `urn:ietf:params:oauth:grant-type:token-exchange`. Must be sent in the request body.""" subject_token_type: Literal["urn:ietf:params:oauth:token-type:id_token"] | None = ( @@ -40,6 +42,7 @@ def from_dict(cls, data: dict[str, Any]) -> TokenQuery: client_secret=data["client_secret"], grant_type=TokenQueryGrantType(data["grant_type"]), code=data.get("code"), + code_verifier=data.get("code_verifier"), subject_token=data.get("subject_token"), subject_token_type=data.get("subject_token_type"), organization_id=data.get("organization_id"), @@ -59,6 +62,8 @@ def to_dict(self) -> dict[str, Any]: ) if self.code is not None: result["code"] = self.code + if self.code_verifier is not None: + result["code_verifier"] = self.code_verifier if self.subject_token is not None: result["subject_token"] = self.subject_token if self.subject_token_type is not None: diff --git a/tests/fixtures/agent_blueprint.json b/tests/fixtures/agent_blueprint.json index 942ec8f2..0f4e69cd 100644 --- a/tests/fixtures/agent_blueprint.json +++ b/tests/fixtures/agent_blueprint.json @@ -15,6 +15,12 @@ "org_01EHWNCE74X7JSDV0X3SZ3KJNY" ] }, + "invocable_as": { + "instance_types": [ + "delegated", + "autonomous" + ] + }, "session_settings": { "max_age_seconds": 3600, "access_token_ttl_seconds": 300, diff --git a/tests/fixtures/agent_blueprint_invocable_as.json b/tests/fixtures/agent_blueprint_invocable_as.json new file mode 100644 index 00000000..bfd33188 --- /dev/null +++ b/tests/fixtures/agent_blueprint_invocable_as.json @@ -0,0 +1,6 @@ +{ + "instance_types": [ + "delegated", + "autonomous" + ] +} diff --git a/tests/fixtures/agent_blueprint_updated.json b/tests/fixtures/agent_blueprint_updated.json index 72b8b717..1c0e8bbf 100644 --- a/tests/fixtures/agent_blueprint_updated.json +++ b/tests/fixtures/agent_blueprint_updated.json @@ -19,6 +19,12 @@ "org_01EHWNCE74X7JSDV0X3SZ3KJNY" ] }, + "invocable_as": { + "instance_types": [ + "delegated", + "autonomous" + ] + }, "session_settings": { "max_age_seconds": 3600, "access_token_ttl_seconds": 300, diff --git a/tests/fixtures/agent_blueprint_updated_data.json b/tests/fixtures/agent_blueprint_updated_data.json index 942ec8f2..0f4e69cd 100644 --- a/tests/fixtures/agent_blueprint_updated_data.json +++ b/tests/fixtures/agent_blueprint_updated_data.json @@ -15,6 +15,12 @@ "org_01EHWNCE74X7JSDV0X3SZ3KJNY" ] }, + "invocable_as": { + "instance_types": [ + "delegated", + "autonomous" + ] + }, "session_settings": { "max_age_seconds": 3600, "access_token_ttl_seconds": 300, diff --git a/tests/fixtures/agent_blueprint_updated_data_invocable_as.json b/tests/fixtures/agent_blueprint_updated_data_invocable_as.json new file mode 100644 index 00000000..bfd33188 --- /dev/null +++ b/tests/fixtures/agent_blueprint_updated_data_invocable_as.json @@ -0,0 +1,6 @@ +{ + "instance_types": [ + "delegated", + "autonomous" + ] +} diff --git a/tests/fixtures/agent_blueprints_update_request.json b/tests/fixtures/agent_blueprints_update_request.json index f7a2ddd1..8d83cb7e 100644 --- a/tests/fixtures/agent_blueprints_update_request.json +++ b/tests/fixtures/agent_blueprints_update_request.json @@ -13,6 +13,12 @@ "org_01EHWNCE74X7JSDV0X3SZ3KJNY" ] }, + "invocable_as": { + "instance_types": [ + "delegated", + "autonomous" + ] + }, "session_settings": { "max_age_seconds": 3600, "access_token_ttl_seconds": 300, diff --git a/tests/fixtures/agent_blueprints_update_request_invocable_as.json b/tests/fixtures/agent_blueprints_update_request_invocable_as.json new file mode 100644 index 00000000..bfd33188 --- /dev/null +++ b/tests/fixtures/agent_blueprints_update_request_invocable_as.json @@ -0,0 +1,6 @@ +{ + "instance_types": [ + "delegated", + "autonomous" + ] +} diff --git a/tests/fixtures/data_integrations_upsert_api_key_request.json b/tests/fixtures/data_integrations_upsert_api_key_request.json index 45952523..6365707c 100644 --- a/tests/fixtures/data_integrations_upsert_api_key_request.json +++ b/tests/fixtures/data_integrations_upsert_api_key_request.json @@ -1,7 +1,8 @@ { + "account_display_name": "Acme production", + "account_identifier": "workspace_123", "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", - "connected_account_id": "data_installation_01EHZNVPK3SFK441A1RGBFSHRT", "connection_owner": "user", "secret": "sk-1234567890abcdef" } diff --git a/tests/fixtures/data_integrations_upsert_api_key_request_2.json b/tests/fixtures/data_integrations_upsert_api_key_request_2.json new file mode 100644 index 00000000..5b41531a --- /dev/null +++ b/tests/fixtures/data_integrations_upsert_api_key_request_2.json @@ -0,0 +1,9 @@ +{ + "account_display_name": "Acme production", + "account_identifier": "workspace_123", + "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + "connection_owner": "user", + "secret": "sk-1234567890abcdef", + "connected_account_id": "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" +} diff --git a/tests/fixtures/directory_sync_rate_limit_error.json b/tests/fixtures/directory_sync_rate_limit_error.json new file mode 100644 index 00000000..4e136b93 --- /dev/null +++ b/tests/fixtures/directory_sync_rate_limit_error.json @@ -0,0 +1,5 @@ +{ + "code": "directory_sync_rate_limited", + "message": "Request could not be processed.", + "retry_after_seconds": 120 +} diff --git a/tests/fixtures/directory_sync_response.json b/tests/fixtures/directory_sync_response.json new file mode 100644 index 00000000..ef041155 --- /dev/null +++ b/tests/fixtures/directory_sync_response.json @@ -0,0 +1,3 @@ +{ + "status": "queued" +} diff --git a/tests/fixtures/portal_link_response.json b/tests/fixtures/portal_link_response.json index 811f32a9..491b08f6 100644 --- a/tests/fixtures/portal_link_response.json +++ b/tests/fixtures/portal_link_response.json @@ -1,3 +1,3 @@ { - "link": "https://setup.workos.com?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + "link": "https://setup.workos.com/init?apiToken=01EHZNVPK3SFK441A1RGBFSHRT" } diff --git a/tests/fixtures/reauthorize_data_integrations_upsert_api_key_request.json b/tests/fixtures/reauthorize_data_integrations_upsert_api_key_request.json new file mode 100644 index 00000000..6912a114 --- /dev/null +++ b/tests/fixtures/reauthorize_data_integrations_upsert_api_key_request.json @@ -0,0 +1,10 @@ +{ + "account_display_name": "Acme production", + "account_identifier": "workspace_123", + "user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + "connection_owner": "user", + "secret": "sk-1234567890abcdef", + "connection_intent": "reauthorize", + "connected_account_id": "data_installation_01EHZNVPK3SFK441A1RGBFSHRT" +} diff --git a/tests/fixtures/token_query.json b/tests/fixtures/token_query.json index 1476f807..1535d163 100644 --- a/tests/fixtures/token_query.json +++ b/tests/fixtures/token_query.json @@ -3,6 +3,7 @@ "client_secret": "sk_example_123456789", "code": "authorization_code_value", "grant_type": "authorization_code", + "code_verifier": "code_verifier_value", "subject_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjMifQ.example", "subject_token_type": "urn:ietf:params:oauth:token-type:id_token", "organization_id": "org_01EHQMYV6MBK39QC5PZXHY59C3" diff --git a/tests/fixtures/vault_dek_rekeyed.json b/tests/fixtures/vault_dek_rekeyed.json new file mode 100644 index 00000000..b4de6e14 --- /dev/null +++ b/tests/fixtures/vault_dek_rekeyed.json @@ -0,0 +1,37 @@ +{ + "object": "event", + "id": "event_01EHZNVPK3SFK441A1RGBFSHRT", + "event": "vault.dek.rekeyed", + "data": { + "actor_id": "user_01EHWNCE74X7JSDV0X3SZ3KJNY", + "actor_source": "api", + "actor_name": "Jane Doe", + "key_ids": [ + "dek_01EHWNCE74X7JSDV0X3SZ3KJNY" + ], + "key_context": { + "key": "test_value" + } + }, + "created_at": "2026-01-15T12:00:00.000Z", + "context": { + "google_analytics_client_id": "GA1.2.1234567890.1234567890", + "google_analytics_sessions": [ + { + "containerId": "GTM-ABCDEF", + "sessionId": "1234567890", + "sessionNumber": "1" + } + ], + "ajs_anonymous_id": "ajs_anon_01EHWNCE74X7JSDV0X3SZ3KJNY", + "client_id": "client_01EHWNCE74X7JSDV0X3SZ3KJNY", + "actor": { + "id": "user_01EHWNCE74X7JSDV0X3SZ3KJNY", + "source": "api", + "name": "Jane Doe" + }, + "previous_attributes": { + "key": {} + } + } +} diff --git a/tests/fixtures/vault_dek_rekeyed_data.json b/tests/fixtures/vault_dek_rekeyed_data.json new file mode 100644 index 00000000..1fcf4c74 --- /dev/null +++ b/tests/fixtures/vault_dek_rekeyed_data.json @@ -0,0 +1,11 @@ +{ + "actor_id": "user_01EHWNCE74X7JSDV0X3SZ3KJNY", + "actor_source": "api", + "actor_name": "Jane Doe", + "key_ids": [ + "dek_01EHWNCE74X7JSDV0X3SZ3KJNY" + ], + "key_context": { + "key": "test_value" + } +} diff --git a/tests/test_agents_models_round_trip.py b/tests/test_agents_models_round_trip.py index ab04e87f..8a5128a1 100644 --- a/tests/test_agents_models_round_trip.py +++ b/tests/test_agents_models_round_trip.py @@ -6,10 +6,13 @@ from workos.agents.models import ( AgentAdminLinkClaimAttemptToExternalUserRequestUser, AgentBlueprint, + AgentBlueprintInvocableAs, AgentBlueprintInvocableBy, + AgentBlueprintsCreateRequestInvocableAs, AgentBlueprintsCreateRequestInvocableBy, AgentBlueprintsCreateRequestSessionSetting, AgentBlueprintSessionSetting, + AgentBlueprintsUpdateRequestInvocableAs, AgentBlueprintsUpdateRequestInvocableBy, AgentBlueprintsUpdateRequestSessionSetting, AgentCredentialValidation, @@ -191,6 +194,7 @@ def test_agent_blueprint_minimal_payload(self): "role_slugs": ["manager"], "organization_ids": ["org_01EHWNCE74X7JSDV0X3SZ3KJNY"], }, + "invocable_as": {"instance_types": ["delegated", "autonomous"]}, "session_settings": { "max_age_seconds": 3600, "access_token_ttl_seconds": 300, @@ -207,6 +211,7 @@ def test_agent_blueprint_minimal_payload(self): assert serialized["description"] == data["description"] assert serialized["permissions"] == data["permissions"] assert serialized["invocable_by"] == data["invocable_by"] + assert serialized["invocable_as"] == data["invocable_as"] assert serialized["session_settings"] == data["session_settings"] assert serialized["created_at"] == data["created_at"] assert serialized["updated_at"] == data["updated_at"] @@ -222,6 +227,7 @@ def test_agent_blueprint_preserves_nullable_fields(self): "role_slugs": ["manager"], "organization_ids": ["org_01EHWNCE74X7JSDV0X3SZ3KJNY"], }, + "invocable_as": {"instance_types": ["delegated", "autonomous"]}, "session_settings": { "max_age_seconds": 3600, "access_token_ttl_seconds": 300, @@ -453,6 +459,20 @@ def test_agent_blueprint_invocable_by_minimal_payload(self): assert serialized["role_slugs"] == data["role_slugs"] assert serialized["organization_ids"] == data["organization_ids"] + def test_agent_blueprint_invocable_as_round_trip(self): + data = load_fixture("agent_blueprint_invocable_as.json") + instance = AgentBlueprintInvocableAs.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = AgentBlueprintInvocableAs.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_agent_blueprint_invocable_as_minimal_payload(self): + data = {"instance_types": ["delegated", "autonomous"]} + instance = AgentBlueprintInvocableAs.from_dict(data) + serialized = instance.to_dict() + assert serialized["instance_types"] == data["instance_types"] + def test_agent_blueprint_session_setting_round_trip(self): data = load_fixture("agent_blueprint_session_setting.json") instance = AgentBlueprintSessionSetting.from_dict(data) @@ -583,6 +603,27 @@ def test_agent_blueprints_create_request_invocable_by_omits_absent_optional_non_ assert "role_slugs" not in serialized assert "organization_ids" not in serialized + def test_agent_blueprints_create_request_invocable_as_round_trip(self): + data = load_fixture("agent_blueprints_create_request_invocable_as.json") + instance = AgentBlueprintsCreateRequestInvocableAs.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = AgentBlueprintsCreateRequestInvocableAs.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_agent_blueprints_create_request_invocable_as_minimal_payload(self): + data = {} + instance = AgentBlueprintsCreateRequestInvocableAs.from_dict(data) + assert instance.to_dict() is not None + + def test_agent_blueprints_create_request_invocable_as_omits_absent_optional_non_nullable_fields( + self, + ): + data = {} + instance = AgentBlueprintsCreateRequestInvocableAs.from_dict(data) + serialized = instance.to_dict() + assert "instance_types" not in serialized + def test_agent_blueprints_create_request_session_setting_round_trip(self): data = load_fixture("agent_blueprints_create_request_session_setting.json") instance = AgentBlueprintsCreateRequestSessionSetting.from_dict(data) @@ -629,6 +670,27 @@ def test_agent_blueprints_update_request_invocable_by_omits_absent_optional_non_ assert "role_slugs" not in serialized assert "organization_ids" not in serialized + def test_agent_blueprints_update_request_invocable_as_round_trip(self): + data = load_fixture("agent_blueprints_update_request_invocable_as.json") + instance = AgentBlueprintsUpdateRequestInvocableAs.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = AgentBlueprintsUpdateRequestInvocableAs.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_agent_blueprints_update_request_invocable_as_minimal_payload(self): + data = {} + instance = AgentBlueprintsUpdateRequestInvocableAs.from_dict(data) + assert instance.to_dict() is not None + + def test_agent_blueprints_update_request_invocable_as_omits_absent_optional_non_nullable_fields( + self, + ): + data = {} + instance = AgentBlueprintsUpdateRequestInvocableAs.from_dict(data) + serialized = instance.to_dict() + assert "instance_types" not in serialized + def test_agent_blueprints_update_request_session_setting_round_trip(self): data = load_fixture("agent_blueprints_update_request_session_setting.json") instance = AgentBlueprintsUpdateRequestSessionSetting.from_dict(data) diff --git a/tests/test_common_models_round_trip.py b/tests/test_common_models_round_trip.py index 21438f6b..14f0eedc 100644 --- a/tests/test_common_models_round_trip.py +++ b/tests/test_common_models_round_trip.py @@ -12,12 +12,14 @@ ActionUserRegistrationDeniedData, AgentBlueprintCreated, AgentBlueprintCreatedData, + AgentBlueprintCreatedDataInvocableAs, AgentBlueprintCreatedDataInvocableBy, AgentBlueprintCreatedDataSessionSetting, AgentBlueprintDeleted, AgentBlueprintDeletedData, AgentBlueprintUpdated, AgentBlueprintUpdatedData, + AgentBlueprintUpdatedDataInvocableAs, AgentBlueprintUpdatedDataInvocableBy, AgentBlueprintUpdatedDataSessionSetting, AgentInstanceCreated, @@ -127,6 +129,7 @@ ConnectionSAMLCertificateRenewedData, ConnectionSAMLCertificateRenewedDataCertificate, ConnectionSAMLCertificateRenewedDataConnection, + DirectorySyncRateLimitError, DirectoryUser, DirectoryUserEmail, DsyncActivated, @@ -304,6 +307,8 @@ VaultDekDecryptedData, VaultDekRead, VaultDekReadData, + VaultDekRekeyed, + VaultDekRekeyedData, VaultKekCreated, VaultKekCreatedData, VaultKekDeleted, @@ -479,6 +484,26 @@ def test_connect_application_m2m_preserves_nullable_fields(self): serialized = instance.to_dict() assert serialized["description"] is None + def test_directory_sync_rate_limit_error_round_trip(self): + data = load_fixture("directory_sync_rate_limit_error.json") + instance = DirectorySyncRateLimitError.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = DirectorySyncRateLimitError.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_directory_sync_rate_limit_error_minimal_payload(self): + data = { + "code": "directory_sync_rate_limited", + "message": "Request could not be processed.", + "retry_after_seconds": 120, + } + instance = DirectorySyncRateLimitError.from_dict(data) + serialized = instance.to_dict() + assert serialized["code"] == data["code"] + assert serialized["message"] == data["message"] + assert serialized["retry_after_seconds"] == data["retry_after_seconds"] + def test_event_context_actor_round_trip(self): data = load_fixture("event_context_actor.json") instance = EventContextActor.from_dict(data) @@ -722,6 +747,8 @@ def test_pipe_connected_account_omits_absent_optional_non_nullable_fields(self): "scopes": ["repo", "user:email"], "api_key_last_4": None, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": None, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", } @@ -745,6 +772,8 @@ def test_pipe_connected_account_preserves_nullable_fields(self): "auth_method": "oauth", "api_key_last_4": None, "state": "connected", + "established_by_user_id": None, + "state_reason": None, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", } @@ -755,6 +784,8 @@ def test_pipe_connected_account_preserves_nullable_fields(self): assert serialized["user_id"] is None assert serialized["organization_id"] is None assert serialized["api_key_last_4"] is None + assert serialized["established_by_user_id"] is None + assert serialized["state_reason"] is None def test_pipe_connected_account_round_trips_unknown_enum_values(self): data = { @@ -771,6 +802,8 @@ def test_pipe_connected_account_round_trips_unknown_enum_values(self): "auth_method": "oauth", "api_key_last_4": None, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": None, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", } @@ -1086,6 +1119,7 @@ def test_agent_blueprint_created_minimal_payload(self): "role_slugs": ["manager"], "organization_ids": ["org_01EHWNCE74X7JSDV0X3SZ3KJNY"], }, + "invocable_as": {"instance_types": ["delegated", "autonomous"]}, "session_settings": { "max_age_seconds": 3600, "access_token_ttl_seconds": 300, @@ -1119,6 +1153,7 @@ def test_agent_blueprint_created_omits_absent_optional_non_nullable_fields(self) "role_slugs": ["manager"], "organization_ids": ["org_01EHWNCE74X7JSDV0X3SZ3KJNY"], }, + "invocable_as": {"instance_types": ["delegated", "autonomous"]}, "session_settings": { "max_age_seconds": 3600, "access_token_ttl_seconds": 300, @@ -1152,6 +1187,7 @@ def test_agent_blueprint_created_data_minimal_payload(self): "role_slugs": ["manager"], "organization_ids": ["org_01EHWNCE74X7JSDV0X3SZ3KJNY"], }, + "invocable_as": {"instance_types": ["delegated", "autonomous"]}, "session_settings": { "max_age_seconds": 3600, "access_token_ttl_seconds": 300, @@ -1168,6 +1204,7 @@ def test_agent_blueprint_created_data_minimal_payload(self): assert serialized["description"] == data["description"] assert serialized["permissions"] == data["permissions"] assert serialized["invocable_by"] == data["invocable_by"] + assert serialized["invocable_as"] == data["invocable_as"] assert serialized["session_settings"] == data["session_settings"] assert serialized["created_at"] == data["created_at"] assert serialized["updated_at"] == data["updated_at"] @@ -1183,6 +1220,7 @@ def test_agent_blueprint_created_data_preserves_nullable_fields(self): "role_slugs": ["manager"], "organization_ids": ["org_01EHWNCE74X7JSDV0X3SZ3KJNY"], }, + "invocable_as": {"instance_types": ["delegated", "autonomous"]}, "session_settings": { "max_age_seconds": 3600, "access_token_ttl_seconds": 300, @@ -1213,6 +1251,20 @@ def test_agent_blueprint_created_data_invocable_by_minimal_payload(self): assert serialized["role_slugs"] == data["role_slugs"] assert serialized["organization_ids"] == data["organization_ids"] + def test_agent_blueprint_created_data_invocable_as_round_trip(self): + data = load_fixture("agent_blueprint_created_data_invocable_as.json") + instance = AgentBlueprintCreatedDataInvocableAs.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = AgentBlueprintCreatedDataInvocableAs.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_agent_blueprint_created_data_invocable_as_minimal_payload(self): + data = {"instance_types": ["delegated", "autonomous"]} + instance = AgentBlueprintCreatedDataInvocableAs.from_dict(data) + serialized = instance.to_dict() + assert serialized["instance_types"] == data["instance_types"] + def test_agent_blueprint_created_data_session_setting_round_trip(self): data = load_fixture("agent_blueprint_created_data_session_setting.json") instance = AgentBlueprintCreatedDataSessionSetting.from_dict(data) @@ -1332,6 +1384,7 @@ def test_agent_blueprint_updated_minimal_payload(self): "role_slugs": ["manager"], "organization_ids": ["org_01EHWNCE74X7JSDV0X3SZ3KJNY"], }, + "invocable_as": {"instance_types": ["delegated", "autonomous"]}, "session_settings": { "max_age_seconds": 3600, "access_token_ttl_seconds": 300, @@ -1365,6 +1418,7 @@ def test_agent_blueprint_updated_omits_absent_optional_non_nullable_fields(self) "role_slugs": ["manager"], "organization_ids": ["org_01EHWNCE74X7JSDV0X3SZ3KJNY"], }, + "invocable_as": {"instance_types": ["delegated", "autonomous"]}, "session_settings": { "max_age_seconds": 3600, "access_token_ttl_seconds": 300, @@ -1398,6 +1452,7 @@ def test_agent_blueprint_updated_data_minimal_payload(self): "role_slugs": ["manager"], "organization_ids": ["org_01EHWNCE74X7JSDV0X3SZ3KJNY"], }, + "invocable_as": {"instance_types": ["delegated", "autonomous"]}, "session_settings": { "max_age_seconds": 3600, "access_token_ttl_seconds": 300, @@ -1414,6 +1469,7 @@ def test_agent_blueprint_updated_data_minimal_payload(self): assert serialized["description"] == data["description"] assert serialized["permissions"] == data["permissions"] assert serialized["invocable_by"] == data["invocable_by"] + assert serialized["invocable_as"] == data["invocable_as"] assert serialized["session_settings"] == data["session_settings"] assert serialized["created_at"] == data["created_at"] assert serialized["updated_at"] == data["updated_at"] @@ -1429,6 +1485,7 @@ def test_agent_blueprint_updated_data_preserves_nullable_fields(self): "role_slugs": ["manager"], "organization_ids": ["org_01EHWNCE74X7JSDV0X3SZ3KJNY"], }, + "invocable_as": {"instance_types": ["delegated", "autonomous"]}, "session_settings": { "max_age_seconds": 3600, "access_token_ttl_seconds": 300, @@ -1459,6 +1516,20 @@ def test_agent_blueprint_updated_data_invocable_by_minimal_payload(self): assert serialized["role_slugs"] == data["role_slugs"] assert serialized["organization_ids"] == data["organization_ids"] + def test_agent_blueprint_updated_data_invocable_as_round_trip(self): + data = load_fixture("agent_blueprint_updated_data_invocable_as.json") + instance = AgentBlueprintUpdatedDataInvocableAs.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = AgentBlueprintUpdatedDataInvocableAs.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_agent_blueprint_updated_data_invocable_as_minimal_payload(self): + data = {"instance_types": ["delegated", "autonomous"]} + instance = AgentBlueprintUpdatedDataInvocableAs.from_dict(data) + serialized = instance.to_dict() + assert serialized["instance_types"] == data["instance_types"] + def test_agent_blueprint_updated_data_session_setting_round_trip(self): data = load_fixture("agent_blueprint_updated_data_session_setting.json") instance = AgentBlueprintUpdatedDataSessionSetting.from_dict(data) @@ -12523,6 +12594,8 @@ def test_pipes_account_connection_connected_minimal_payload(self): "auth_method": "oauth", "api_key_last_4": None, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": None, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", }, @@ -12557,6 +12630,8 @@ def test_pipes_account_connection_connected_omits_absent_optional_non_nullable_f "auth_method": "oauth", "api_key_last_4": None, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": None, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", }, @@ -12717,6 +12792,8 @@ def test_pipes_account_connection_disconnected_minimal_payload(self): "auth_method": "oauth", "api_key_last_4": None, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": None, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", }, @@ -12751,6 +12828,8 @@ def test_pipes_account_connection_disconnected_omits_absent_optional_non_nullabl "auth_method": "oauth", "api_key_last_4": None, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": None, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", }, @@ -12787,6 +12866,8 @@ def test_pipes_account_connection_reauthorization_needed_minimal_payload(self): "auth_method": "oauth", "api_key_last_4": None, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": None, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", }, @@ -12821,6 +12902,8 @@ def test_pipes_account_connection_reauthorization_needed_omits_absent_optional_n "auth_method": "oauth", "api_key_last_4": None, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": None, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", }, @@ -12857,6 +12940,8 @@ def test_pipes_connected_account_connected_minimal_payload(self): "auth_method": "oauth", "api_key_last_4": None, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": None, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", }, @@ -12891,6 +12976,8 @@ def test_pipes_connected_account_connected_omits_absent_optional_non_nullable_fi "auth_method": "oauth", "api_key_last_4": None, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": None, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", }, @@ -13051,6 +13138,8 @@ def test_pipes_connected_account_disconnected_minimal_payload(self): "auth_method": "oauth", "api_key_last_4": None, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": None, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", }, @@ -13085,6 +13174,8 @@ def test_pipes_connected_account_disconnected_omits_absent_optional_non_nullable "auth_method": "oauth", "api_key_last_4": None, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": None, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", }, @@ -13121,6 +13212,8 @@ def test_pipes_connected_account_reauthorization_needed_minimal_payload(self): "auth_method": "oauth", "api_key_last_4": None, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": None, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", }, @@ -13155,6 +13248,8 @@ def test_pipes_connected_account_reauthorization_needed_omits_absent_optional_no "auth_method": "oauth", "api_key_last_4": None, "state": "connected", + "established_by_user_id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "state_reason": None, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", }, @@ -14989,6 +15084,89 @@ def test_vault_dek_read_data_round_trips_unknown_enum_values(self): instance = VaultDekReadData.from_dict(data) assert instance.to_dict() == data + def test_vault_dek_rekeyed_round_trip(self): + data = load_fixture("vault_dek_rekeyed.json") + instance = VaultDekRekeyed.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = VaultDekRekeyed.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_vault_dek_rekeyed_minimal_payload(self): + data = { + "object": "event", + "id": "event_01EHZNVPK3SFK441A1RGBFSHRT", + "event": "vault.dek.rekeyed", + "data": { + "actor_id": "user_01EHWNCE74X7JSDV0X3SZ3KJNY", + "actor_source": "api", + "actor_name": "Jane Doe", + "key_ids": ["dek_01EHWNCE74X7JSDV0X3SZ3KJNY"], + "key_context": {"key": "test_value"}, + }, + "created_at": "2026-01-15T12:00:00.000Z", + } + instance = VaultDekRekeyed.from_dict(data) + serialized = instance.to_dict() + assert serialized["object"] == data["object"] + assert serialized["id"] == data["id"] + assert serialized["event"] == data["event"] + assert serialized["data"] == data["data"] + assert serialized["created_at"] == data["created_at"] + + def test_vault_dek_rekeyed_omits_absent_optional_non_nullable_fields(self): + data = { + "object": "event", + "id": "event_01EHZNVPK3SFK441A1RGBFSHRT", + "event": "vault.dek.rekeyed", + "data": { + "actor_id": "user_01EHWNCE74X7JSDV0X3SZ3KJNY", + "actor_source": "api", + "actor_name": "Jane Doe", + "key_ids": ["dek_01EHWNCE74X7JSDV0X3SZ3KJNY"], + "key_context": {"key": "test_value"}, + }, + "created_at": "2026-01-15T12:00:00.000Z", + } + instance = VaultDekRekeyed.from_dict(data) + serialized = instance.to_dict() + assert "context" not in serialized + + def test_vault_dek_rekeyed_data_round_trip(self): + data = load_fixture("vault_dek_rekeyed_data.json") + instance = VaultDekRekeyedData.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = VaultDekRekeyedData.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_vault_dek_rekeyed_data_minimal_payload(self): + data = { + "actor_id": "user_01EHWNCE74X7JSDV0X3SZ3KJNY", + "actor_source": "api", + "actor_name": "Jane Doe", + "key_ids": ["dek_01EHWNCE74X7JSDV0X3SZ3KJNY"], + "key_context": {"key": "test_value"}, + } + instance = VaultDekRekeyedData.from_dict(data) + serialized = instance.to_dict() + assert serialized["actor_id"] == data["actor_id"] + assert serialized["actor_source"] == data["actor_source"] + assert serialized["actor_name"] == data["actor_name"] + assert serialized["key_ids"] == data["key_ids"] + assert serialized["key_context"] == data["key_context"] + + def test_vault_dek_rekeyed_data_round_trips_unknown_enum_values(self): + data = { + "actor_id": "user_01EHWNCE74X7JSDV0X3SZ3KJNY", + "actor_source": "unexpected_vault_dek_rekeyed_data_actor_source", + "actor_name": "Jane Doe", + "key_ids": ["dek_01EHWNCE74X7JSDV0X3SZ3KJNY"], + "key_context": {"key": "test_value"}, + } + instance = VaultDekRekeyedData.from_dict(data) + assert instance.to_dict() == data + def test_vault_kek_created_round_trip(self): data = load_fixture("vault_kek_created.json") instance = VaultKekCreated.from_dict(data) @@ -15474,38 +15652,38 @@ def test_waitlist_user_denied_omits_absent_optional_non_nullable_fields(self): serialized = instance.to_dict() assert "context" not in serialized - def test_account_selection_required_error_round_trip(self): - data = load_fixture("account_selection_required_error.json") - instance = AccountSelectionRequiredError.from_dict(data) + def test_auth_method_mismatch_error_round_trip(self): + data = load_fixture("auth_method_mismatch_error.json") + instance = AuthMethodMismatchError.from_dict(data) serialized = instance.to_dict() assert serialized == data - restored = AccountSelectionRequiredError.from_dict(serialized) + restored = AuthMethodMismatchError.from_dict(serialized) assert restored.to_dict() == serialized - def test_account_selection_required_error_minimal_payload(self): + def test_auth_method_mismatch_error_minimal_payload(self): data = { - "code": "account_selection_required", - "message": "Several connected accounts match this user for this provider. Name one with a connected account id.", + "code": "auth_method_mismatch", + "message": "This installation uses oauth authentication. Use the POST /:slug/token endpoint instead.", } - instance = AccountSelectionRequiredError.from_dict(data) + instance = AuthMethodMismatchError.from_dict(data) serialized = instance.to_dict() assert serialized["code"] == data["code"] assert serialized["message"] == data["message"] - def test_auth_method_mismatch_error_round_trip(self): - data = load_fixture("auth_method_mismatch_error.json") - instance = AuthMethodMismatchError.from_dict(data) + def test_account_selection_required_error_round_trip(self): + data = load_fixture("account_selection_required_error.json") + instance = AccountSelectionRequiredError.from_dict(data) serialized = instance.to_dict() assert serialized == data - restored = AuthMethodMismatchError.from_dict(serialized) + restored = AccountSelectionRequiredError.from_dict(serialized) assert restored.to_dict() == serialized - def test_auth_method_mismatch_error_minimal_payload(self): + def test_account_selection_required_error_minimal_payload(self): data = { - "code": "auth_method_mismatch", - "message": "This installation uses oauth authentication. Use the POST /:slug/token endpoint instead.", + "code": "account_selection_required", + "message": "Several connected accounts match this user for this provider. Name one with a connected account id.", } - instance = AuthMethodMismatchError.from_dict(data) + instance = AccountSelectionRequiredError.from_dict(data) serialized = instance.to_dict() assert serialized["code"] == data["code"] assert serialized["message"] == data["message"] diff --git a/tests/test_directory_sync.py b/tests/test_directory_sync.py index ba71e0d3..68bf109c 100644 --- a/tests/test_directory_sync.py +++ b/tests/test_directory_sync.py @@ -15,7 +15,11 @@ ) from workos._pagination import AsyncPage, SyncPage from workos.common.models import DirectoryGroup, PaginationOrder -from workos.directory_sync.models import Directory, DirectoryUserWithGroups +from workos.directory_sync.models import ( + Directory, + DirectorySyncResponse, + DirectoryUserWithGroups, +) class TestDirectorySync: @@ -74,6 +78,17 @@ def test_delete_directory(self, workos, httpx_mock): assert request.method == "DELETE" assert request.url.path.endswith("/directories/test_id") + def test_create_directory_sync(self, workos, httpx_mock): + httpx_mock.add_response( + json=load_fixture("directory_sync_response.json"), + ) + result = workos.directory_sync.create_directory_sync("test_id") + assert isinstance(result, DirectorySyncResponse) + assert result.status == "queued" + request = httpx_mock.get_request() + assert request.method == "POST" + assert request.url.path.endswith("/directories/test_id/sync") + def test_list_groups(self, workos, httpx_mock): httpx_mock.add_response( json=load_fixture("list_directory_group.json"), @@ -303,6 +318,16 @@ async def test_delete_directory(self, async_workos, httpx_mock): assert request.method == "DELETE" assert request.url.path.endswith("/directories/test_id") + @pytest.mark.asyncio + async def test_create_directory_sync(self, async_workos, httpx_mock): + httpx_mock.add_response(json=load_fixture("directory_sync_response.json")) + result = await async_workos.directory_sync.create_directory_sync("test_id") + assert isinstance(result, DirectorySyncResponse) + assert result.status == "queued" + request = httpx_mock.get_request() + assert request.method == "POST" + assert request.url.path.endswith("/directories/test_id/sync") + @pytest.mark.asyncio async def test_list_groups(self, async_workos, httpx_mock): httpx_mock.add_response(json=load_fixture("list_directory_group.json")) diff --git a/tests/test_directory_sync_models_round_trip.py b/tests/test_directory_sync_models_round_trip.py index dc7215e2..3864abaf 100644 --- a/tests/test_directory_sync_models_round_trip.py +++ b/tests/test_directory_sync_models_round_trip.py @@ -8,6 +8,7 @@ DirectoryGroup, DirectoryMetadata, DirectoryMetadataUser, + DirectorySyncResponse, DirectoryUserWithGroups, DirectoryUserWithGroupsEmail, ) @@ -80,6 +81,20 @@ def test_directory_round_trips_unknown_enum_values(self): instance = Directory.from_dict(data) assert instance.to_dict() == data + def test_directory_sync_response_round_trip(self): + data = load_fixture("directory_sync_response.json") + instance = DirectorySyncResponse.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = DirectorySyncResponse.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_directory_sync_response_minimal_payload(self): + data = {"status": "queued"} + instance = DirectorySyncResponse.from_dict(data) + serialized = instance.to_dict() + assert serialized["status"] == data["status"] + def test_directory_group_round_trip(self): data = load_fixture("directory_group.json") instance = DirectoryGroup.from_dict(data) From 490196e353a72d4e3145a7acea022f14c32b2895 Mon Sep 17 00:00:00 2001 From: "workos-sdk-automation[bot]" <255426317+workos-sdk-automation[bot]@users.noreply.github.com> Date: Wed, 14 Oct 2026 21:31:44 +0000 Subject: [PATCH 8/8] chore(generated): add release notes fragment --- ...47ddc7441c12b312c7308b3d470da87d04bf729.md | 186 ++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 .changelog-pending/2026-10-14T21-31-44-247ddc7441c12b312c7308b3d470da87d04bf729.md diff --git a/.changelog-pending/2026-10-14T21-31-44-247ddc7441c12b312c7308b3d470da87d04bf729.md b/.changelog-pending/2026-10-14T21-31-44-247ddc7441c12b312c7308b3d470da87d04bf729.md new file mode 100644 index 00000000..6c6938e4 --- /dev/null +++ b/.changelog-pending/2026-10-14T21-31-44-247ddc7441c12b312c7308b3d470da87d04bf729.md @@ -0,0 +1,186 @@ +* [#747](https://github.com/workos/workos-python/pull/747) feat(generated)!: regenerate from spec (9 changes) + + **⚠️ Breaking** + * **[pipes](https://workos.com/docs/reference/pipes)**: + * Removed model `AddDataIntegrationsUpsertApiKeyRequest` + * Removed model `AddDataIntegrationsUpsertClientCredentialsRequest` + * Removed enum `AddDataIntegrationsUpsertApiKeyRequestConnectionOwner` + * Removed enum `AddDataIntegrationsUpsertClientCredentialsRequestConnectionOwner` + + **Features** + * **[admin_portal](https://workos.com/docs/reference/admin-portal)**: + * Added model `RevokeLink` + * Added `external_user_id` to `GenerateLink` + * Added endpoint `POST /portal/revoke_link` + * Changed errors for endpoint `POST /portal/generate_link` + * **[agents](https://workos.com/docs/reference/agents)**: + * Added model `AgentBlueprintInvocableAs` + * Added model `AgentBlueprintsCreateRequestInvocableAs` + * Added model `AgentBlueprintsUpdateRequestInvocableAs` + * Added `invocable_as` to `AgentBlueprint` + * Added `invocable_as` to `AgentBlueprintsCreateRequest` + * Added `invocable_as` to `AgentBlueprintsUpdateRequest` + * Added enum `AgentBlueprintInvocableAsInstanceTypes` + * Added enum `AgentBlueprintsCreateRequestInvocableAsInstanceTypes` + * Added enum `AgentBlueprintsUpdateRequestInvocableAsInstanceTypes` + * **[audit_logs](https://workos.com/docs/reference/audit-logs)**: + * Changed errors for endpoint `GET /audit_logs/actions` + * Changed errors for endpoint `GET /audit_logs/actions/{actionName}/schemas` + * Changed errors for endpoint `POST /audit_logs/actions/{actionName}/schemas` + * Changed errors for endpoint `POST /audit_logs/events` + * Changed errors for endpoint `POST /audit_logs/exports` + * Changed errors for endpoint `GET /audit_logs/exports/{auditLogExportId}` + * **[authorization](https://workos.com/docs/reference/fga)**: + * Changed errors for endpoint `GET /authorization/organization_memberships/{organization_membership_id}/role_assignments` + * Changed errors for endpoint `POST /authorization/groups/{group_id}/role_assignments` + * Changed errors for endpoint `PUT /authorization/groups/{group_id}/role_assignments` + * Changed errors for endpoint `GET /authorization/groups/{group_id}/role_assignments/{role_assignment_id}` + * Changed errors for endpoint `POST /authorization/organization_memberships/{organization_membership_id}/check` + * Changed errors for endpoint `GET /authorization/organization_memberships/{organization_membership_id}/resources` + * Changed errors for endpoint `GET /authorization/organization_memberships/{organization_membership_id}/resources/{resource_id}/permissions` + * Changed errors for endpoint `GET /authorization/organization_memberships/{organization_membership_id}/resources/{resource_type_slug}/{external_id}/permissions` + * Changed errors for endpoint `POST /authorization/organization_memberships/{organization_membership_id}/role_assignments` + * Changed errors for endpoint `GET /authorization/organizations/{organization_id}/resources/{resource_type_slug}/{external_id}` + * Changed errors for endpoint `PATCH /authorization/organizations/{organization_id}/resources/{resource_type_slug}/{external_id}` + * Changed errors for endpoint `GET /authorization/organizations/{organization_id}/resources/{resource_type_slug}/{external_id}/organization_memberships` + * Changed errors for endpoint `GET /authorization/organizations/{organization_id}/resources/{resource_type_slug}/{external_id}/role_assignments` + * Changed errors for endpoint `GET /authorization/resources` + * Changed errors for endpoint `POST /authorization/resources` + * Changed errors for endpoint `GET /authorization/resources/{resource_id}` + * Changed errors for endpoint `PATCH /authorization/resources/{resource_id}` + * Changed errors for endpoint `GET /authorization/resources/{resource_id}/organization_memberships` + * Changed errors for endpoint `GET /authorization/resources/{resource_id}/role_assignments` + * **[directory_sync](https://workos.com/docs/reference/directory-sync)**: + * Added model `DirectorySyncResponse` + * Added model `DirectorySyncRateLimitError` + * Added endpoint `POST /directories/{id}/sync` + * **[feature_flags](https://workos.com/docs/reference/feature-flags)**: + * Changed errors for endpoint `PUT /feature-flags/{slug}/disable` + * Changed errors for endpoint `PUT /feature-flags/{slug}/enable` + * **[organization_membership](https://workos.com/docs/reference/authkit/organization-membership)**: + * Changed errors for endpoint `DELETE /user_management/organization_memberships/{id}` + * **[organizations](https://workos.com/docs/reference/organization)**: + * Changed errors for endpoint `GET /organizations/{id}/audit_logs_retention` + * Changed errors for endpoint `PUT /organizations/{id}/audit_logs_retention` + * Changed errors for endpoint `POST /organizations/{organization_id}/it_contacts/{contact_id}/invite` + * **[pipes](https://workos.com/docs/reference/pipes)**: + * Added `established_by_user_id` to `ConnectedAccountWriteResponse` + * Added `state_reason` to `ConnectedAccountWriteResponse` + * Added `established_by_user_id` to `ConnectedAccount` + * Added `state_reason` to `ConnectedAccount` + * Added `established_by_user_id` to `DataIntegrationsListResponseDataConnectedAccount` + * Added `state_reason` to `DataIntegrationsListResponseDataConnectedAccount` + * Changed idempotency-key contract for endpoint `POST /data-integrations/{slug}/api-key` + * Changed idempotency-key contract for endpoint `POST /data-integrations/{slug}/client-credentials` + * Changed idempotency-key contract for endpoint `POST /organizations/{organization_id}/connected_accounts/{slug}` + * Changed idempotency-key contract for endpoint `POST /user_management/users/{user_id}/connected_accounts/{slug}` + * Added model `ConnectedAccountWriteResponse` + * Added model `ConnectedAccountWriteResponseWarning` + * Added `account_display_name` to `CreateOrganizationConnectedAccount` + * Added `account_identifier` to `CreateOrganizationConnectedAccount` + * Added `account_display_name` to `OrganizationConnectedAccount` + * Added `account_identifier` to `OrganizationConnectedAccount` + * Added `account_display_name` to `CreateConnectedAccount` + * Added `account_identifier` to `CreateConnectedAccount` + * Added `account_display_name` to `ConnectedAccountInput` + * Added `account_identifier` to `ConnectedAccountInput` + * Added `account_display_name` to `DataIntegrationsCreateApiKeyConnectionRequest` + * Added `account_identifier` to `DataIntegrationsCreateApiKeyConnectionRequest` + * Added `account_display_name` to `DataIntegrationsUpsertApiKeyRequest` + * Added `account_identifier` to `DataIntegrationsUpsertApiKeyRequest` + * Added `account_display_name` to `ReauthorizeDataIntegrationsUpsertApiKeyRequest` + * Added `account_identifier` to `ReauthorizeDataIntegrationsUpsertApiKeyRequest` + * Added `account_display_name` to `DataIntegrationsUpsertApiKeyRequest2` + * Added `account_identifier` to `DataIntegrationsUpsertApiKeyRequest2` + * Added `account_display_name` to `DataIntegrationsGetDataIntegrationAuthorizeUrlRequest` + * Added `account_display_name` to `AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequest` + * Added `account_display_name` to `ReauthorizeDataIntegrationsGetDataIntegrationAuthorizeUrlRequest` + * Added `account_display_name` to `DataIntegrationsCreateClientCredentialsConnectionRequest` + * Added `account_identifier` to `DataIntegrationsCreateClientCredentialsConnectionRequest` + * Added `account_display_name` to `DataIntegrationsUpsertClientCredentialsRequest` + * Added `account_identifier` to `DataIntegrationsUpsertClientCredentialsRequest` + * Added `account_display_name` to `ReauthorizeDataIntegrationsUpsertClientCredentialsRequest` + * Added `account_identifier` to `ReauthorizeDataIntegrationsUpsertClientCredentialsRequest` + * Added `account_display_name` to `DataIntegrationsUpsertClientCredentialsRequest2` + * Added `account_identifier` to `DataIntegrationsUpsertClientCredentialsRequest2` + * Added enum `ConnectedAccountWriteResponseConnectionRole` + * Added enum `ConnectedAccountWriteResponseAuthMethod` + * Added enum `ConnectedAccountWriteResponseState` + * Changed errors for endpoint `POST /data-integrations/{slug}/api-key` + * Changed errors for endpoint `POST /data-integrations/{slug}/authorize` + * Changed errors for endpoint `POST /data-integrations/{slug}/client-credentials` + * Added model `AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequest` + * Added model `ReauthorizeDataIntegrationsGetDataIntegrationAuthorizeUrlRequest` + * Added enum `AddDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner` + * Added enum `ReauthorizeDataIntegrationsGetDataIntegrationAuthorizeUrlRequestConnectionOwner` + * Added model `DataIntegrationsCreateApiKeyConnectionRequest` + * Added model `DataIntegrationsCreateClientCredentialsConnectionRequest` + * Added enum `DataIntegrationsCreateApiKeyConnectionRequestConnectionOwner` + * Added enum `DataIntegrationsCreateClientCredentialsConnectionRequestConnectionOwner` + * Added endpoint `POST /data-integrations/{slug}/api-key` + * Added endpoint `POST /data-integrations/{slug}/client-credentials` + * Added `config` to `DataIntegrationVendedCredential` + * Added model `DataIntegrationVendedCredential` + * Added model `CreateOrganizationConnectedAccount` + * Added model `CreateConnectedAccount` + * Added model `AddDataIntegrationsUpsertApiKeyRequest` + * Added model `ReauthorizeDataIntegrationsUpsertApiKeyRequest` + * Added model `DataIntegrationsUpsertApiKeyRequest2` + * Added model `AddDataIntegrationsUpsertClientCredentialsRequest` + * Added model `ReauthorizeDataIntegrationsUpsertClientCredentialsRequest` + * Added model `DataIntegrationsUpsertClientCredentialsRequest2` + * Added enum `CreateOrganizationConnectedAccountState` + * Added enum `CreateConnectedAccountState` + * Added enum `AddDataIntegrationsUpsertApiKeyRequestConnectionOwner` + * Added enum `ReauthorizeDataIntegrationsUpsertApiKeyRequestConnectionOwner` + * Added enum `DataIntegrationsUpsertApiKeyRequest2ConnectionOwner` + * Added enum `AddDataIntegrationsUpsertClientCredentialsRequestConnectionOwner` + * Added enum `ReauthorizeDataIntegrationsUpsertClientCredentialsRequestConnectionOwner` + * Added enum `DataIntegrationsUpsertClientCredentialsRequest2ConnectionOwner` + * Added parameter `OrganizationsDataProviders.updateOrganizationDataInstallation.connection_intent` + * Added parameter `UserManagementDataProviders.updateUserDataInstallation.connection_intent` + * Added model `OrganizationConnectedAccount` + * Added enum `OrganizationConnectedAccountState` + * **[radar](https://workos.com/docs/reference/radar)**: + * Changed errors for endpoint `POST /radar/attempts` + * Changed errors for endpoint `PUT /radar/attempts/{id}` + * Changed errors for endpoint `POST /radar/lists/{type}/{action}` + * **[sso](https://workos.com/docs/reference/sso)**: + * Added `code_verifier` to `TokenQuery` + * **[user_management](https://workos.com/docs/reference/authkit/user)**: + * Changed errors for endpoint `POST /user_management/invitations` + * Changed errors for endpoint `POST /user_management/invitations/{id}/resend` + * Changed errors for endpoint `POST /user_management/waitlist_entries/{id}/approve` + * Changed errors for endpoint `PUT /user_management/users/{id}` + + **Fixes** + * **[agents](https://workos.com/docs/reference/agents)**: + * Changed errors for endpoint `POST /agents/blueprints/{agent_blueprint_id}/tokens` + * **[pipes](https://workos.com/docs/reference/pipes)**: + * Changed response of `Pipes.createApiKeyConnection` from `ConnectedAccount` to `ConnectedAccountWriteResponse` + * Changed response of `Pipes.upsertApiKey` from `ConnectedAccount` to `ConnectedAccountWriteResponse` + * Changed response of `Pipes.createClientCredentialsConnection` from `ConnectedAccount` to `ConnectedAccountWriteResponse` + * Changed response of `Pipes.upsertClientCredentials` from `ConnectedAccount` to `ConnectedAccountWriteResponse` + * Changed response of `OrganizationsDataProviders.createOrganizationDataInstallation` from `ConnectedAccount` to `ConnectedAccountWriteResponse` + * Changed response of `OrganizationsDataProviders.updateOrganizationDataInstallation` from `ConnectedAccount` to `ConnectedAccountWriteResponse` + * Changed response of `UserManagementDataProviders.createUserDataInstallation` from `ConnectedAccount` to `ConnectedAccountWriteResponse` + * Changed response of `UserManagementDataProviders.updateUserDataInstallation` from `ConnectedAccount` to `ConnectedAccountWriteResponse` + * Changed errors for endpoint `POST /organizations/{organization_id}/connected_accounts/{slug}` + * Changed errors for endpoint `POST /user_management/users/{user_id}/connected_accounts/{slug}` + * Changed errors for endpoint `PUT /data-integrations/{slug}/api-key` + * Changed request body for `Pipes.getDataIntegrationAuthorizeUrl` + * Changed errors for endpoint `POST /data-integrations/{slug}/authorize` + * Changed errors for endpoint `PUT /data-integrations/{slug}/client-credentials` + * Changed request body for `Pipes.upsertApiKey` + * Changed request body for `Pipes.upsertClientCredentials` + * Changed the type of `DataIntegrationCredentialsResponse.credential` + * Removed `connected_account_id` from `DataIntegrationsUpsertApiKeyRequest` + * Removed `connected_account_id` from `DataIntegrationsUpsertClientCredentialsRequest` + * Changed request body of `OrganizationsDataProviders.createOrganizationDataInstallation` from `OrganizationConnectedAccount` to `CreateOrganizationConnectedAccount` + * Changed request body of `UserManagementDataProviders.createUserDataInstallation` from `ConnectedAccountInput` to `CreateConnectedAccount` + * Changed request body of `OrganizationsDataProviders.createOrganizationDataInstallation` from `ConnectedAccountInput` to `OrganizationConnectedAccount` + * Changed request body of `OrganizationsDataProviders.updateOrganizationDataInstallation` from `ConnectedAccountInput` to `OrganizationConnectedAccount` + * Changed errors for endpoint `PUT /organizations/{organization_id}/connected_accounts/{slug}` + * **[sso](https://workos.com/docs/reference/sso)**: + * Changed errors for endpoint `POST /connections` + * Changed errors for endpoint `POST /connections/{connectionId}/saml_sp_signing_cert`