From a0fcb4881c754959f9c4bd8d3842c91dbe962150 Mon Sep 17 00:00:00 2001 From: "workos-sdk-automation[bot]" <255426317+workos-sdk-automation[bot]@users.noreply.github.com> Date: Mon, 31 Aug 2026 20:17:46 +0000 Subject: [PATCH 1/3] feat(user_management)!: SDK surface change: Symbol "CreatePasswordResetToken" was removed --- src/workos/user_management/_resource.py | 547 +++++++++++++++++- src/workos/user_management/models/__init__.py | 6 + .../models/create_password_reset_token.py | 31 +- .../models/create_waitlist_entry.py | 42 ++ .../models/user_management_waitlists_state.py | 30 + src/workos/user_management/models/waitlist.py | 45 ++ .../user_management/models/waitlist_entry.py | 78 +++ tests/test_user_management.py | 239 +++++++- .../test_user_management_models_round_trip.py | 99 ++++ 9 files changed, 1083 insertions(+), 34 deletions(-) create mode 100644 src/workos/user_management/models/create_waitlist_entry.py create mode 100644 src/workos/user_management/models/user_management_waitlists_state.py create mode 100644 src/workos/user_management/models/waitlist.py create mode 100644 src/workos/user_management/models/waitlist_entry.py diff --git a/src/workos/user_management/_resource.py b/src/workos/user_management/_resource.py index fb8bb9a3..a5c2a2d7 100644 --- a/src/workos/user_management/_resource.py +++ b/src/workos/user_management/_resource.py @@ -69,7 +69,10 @@ UserInvite, UserManagementAuthenticationProvider, UserManagementAuthenticationScreenHint, + UserManagementWaitlistsState, VerifyEmailResponse, + Waitlist, + WaitlistEntry, ) @@ -1324,7 +1327,6 @@ def delete_user( 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. @@ -2169,6 +2171,277 @@ def delete_user_authorized_application( request_options=request_options, ) + def delete_waitlist_entry( + self, + id: str, + *, + request_options: RequestOptions | None = None, + ) -> None: + """Delete a waitlist entry + + Remove the entry from the waitlist. Its email address can join again unless a user with that email now exists in the environment. Deleting the entry does not revoke an invitation created by approving it — [revoke that invitation](https://workos.com/docs/reference/authkit/invitation/revoke) separately to withdraw access. + + Args: + id: The unique ID of the waitlist entry. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Raises: + NotFoundError: If the resource is not found (404). + AuthenticationError: If the API key is invalid (401). + RateLimitExceededError: If rate limited (429). + ServerError: If the server returns a 5xx error. + """ + self._client.request( + method="delete", + path=("user_management", "waitlist_entries", str(id)), + request_options=request_options, + ) + + def create_waitlist_entry_approve( + self, + id: str, + *, + request_options: RequestOptions | None = None, + ) -> WaitlistEntry: + """Approve a waitlist entry + + Approve a waitlist entry, create an invitation for its email address, and send the invitation email. Approving a denied entry reverses the denial. The approval is saved even when the invitation steps fail, so instead of retrying the approval, recover based on the outcome: + + - `200` — the entry is approved. If invitation creation failed, no invitation exists yet; [send](https://workos.com/docs/reference/authkit/invitation/send) one. + - `422` with code `invitation_email_not_sent` — the entry is approved and an invitation exists, but its email was not sent; [resend](https://workos.com/docs/reference/authkit/invitation/resend) it. + - `422` with code `invalid_state` — the entry was already approved. + + Args: + id: The unique ID of the waitlist entry. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + WaitlistEntry + + 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. + """ + return self._client.request( + method="post", + path=("user_management", "waitlist_entries", str(id), "approve"), + model=WaitlistEntry, + request_options=request_options, + ) + + def create_waitlist_entry_deny( + self, + id: str, + *, + request_options: RequestOptions | None = None, + ) -> WaitlistEntry: + """Deny a waitlist entry + + Deny a pending waitlist entry. Denying an entry that is not pending fails with the code `invalid_state`. A denial can be reversed by approving the entry. + + Args: + id: The unique ID of the waitlist entry. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + WaitlistEntry + + 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. + """ + return self._client.request( + method="post", + path=("user_management", "waitlist_entries", str(id), "deny"), + model=WaitlistEntry, + request_options=request_options, + ) + + def list_waitlists( + self, + *, + limit: int | None = None, + before: str | None = None, + after: str | None = None, + order: str | None = None, + request_options: RequestOptions | None = None, + ) -> SyncPage[Waitlist]: + """List waitlists + + Get a list of the waitlists in the environment. All waitlists are returned in a single response — this endpoint is not paginated, so the `list_metadata` cursors are always `null`. + + Args: + limit: The limit. + before: The before. + after: The after. + order: The order. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + SyncPage[Waitlist] + + Raises: + AuthenticationError: If the API key is invalid (401). + RateLimitExceededError: If rate limited (429). + ServerError: If the server returns a 5xx error. + """ + params = { + k: v + for k, v in { + "limit": limit, + "before": before, + "after": after, + "order": order, + }.items() + if v is not None + } + return self._client.request_page( + method="get", + path=("user_management", "waitlists"), + model=Waitlist, + params=params, + request_options=request_options, + ) + + def get_waitlist( + self, + id: str, + *, + request_options: RequestOptions | None = None, + ) -> Waitlist: + """Get a waitlist + + Get the details of an existing waitlist. + + Args: + id: The unique ID of the waitlist, or the literal `default` for the environment's default waitlist. The default waitlist is created when its first entry is added, so read requests for `default` return a `404` until then. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + Waitlist + + Raises: + NotFoundError: If the resource is not found (404). + AuthenticationError: If the API key is invalid (401). + RateLimitExceededError: If rate limited (429). + ServerError: If the server returns a 5xx error. + """ + return self._client.request( + method="get", + path=("user_management", "waitlists", str(id)), + model=Waitlist, + request_options=request_options, + ) + + def list_waitlist_entries( + self, + id: str, + *, + limit: int | None = None, + before: str | None = None, + after: str | None = None, + order: PaginationOrder | str | None = "desc", + state: UserManagementWaitlistsState | str | None = None, + email: str | None = None, + request_options: RequestOptions | None = None, + ) -> SyncPage[WaitlistEntry]: + """List waitlist entries + + Get a list of entries on a waitlist matching the criteria specified. + + Args: + id: The unique ID of the waitlist, or the literal `default` for the environment's default waitlist. The default waitlist is created when its first entry is added, so read requests for `default` return a `404` until then. + limit: Upper limit on the number of objects to return, between `1` and `100`. Defaults to `10`. + before: An object ID that defines your place in the list. When the ID is not present, you are at the end of the list. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. + after: An object ID that defines your place in the list. When the ID is not present, you are at the end of the list. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to `desc`. + state: Filter waitlist entries by their state. + email: Filter waitlist entries by their exact email address. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + SyncPage[WaitlistEntry] + + 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. + """ + params = { + k: v + for k, v in { + "limit": limit, + "before": before, + "after": after, + "order": enum_value(order) if order is not None else None, + "state": enum_value(state) if state is not None else None, + "email": email, + }.items() + if v is not None + } + return self._client.request_page( + method="get", + path=("user_management", "waitlists", str(id), "entries"), + model=WaitlistEntry, + params=params, + request_options=request_options, + ) + + def create_waitlist_entry( + self, + id: str, + *, + email: str, + additional_fields: dict[str, str] | None = None, + send_confirmation_email: bool | None = None, + request_options: RequestOptions | None = None, + ) -> WaitlistEntry: + """Create a waitlist entry + + Add an email address to the waitlist. Email addresses are normalized and unique per environment: a request for an email address already on the waitlist returns the existing entry unchanged (still with status `201`) and does not send another confirmation email. If a user with the email address already exists in the environment, the request fails with the code `user_already_exists`. + + Args: + id: The unique ID of the waitlist, or the literal `default` for the environment's default waitlist. Use `default` when adding the first entry — the default waitlist is created automatically. + email: The email address of the user joining the waitlist. + additional_fields: Object containing additional key/value pairs collected with the waitlist entry. Supports up to 50 string pairs, with keys up to 40 characters and values up to 600 characters. Values are user-provided — treat them as untrusted input when rendering or exporting. + send_confirmation_email: Whether to send the waitlist confirmation email to the user. Defaults to `false`. No email is sent when the waitlist confirmation email is disabled in the environment, even if `send_confirmation_email` is `true`. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + WaitlistEntry + + 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] = { + k: v + for k, v in { + "email": email, + "additional_fields": additional_fields, + "send_confirmation_email": send_confirmation_email, + }.items() + if v is not None + } + return self._client.request( + method="post", + path=("user_management", "waitlists", str(id), "entries"), + body=body, + model=WaitlistEntry, + request_options=request_options, + ) + def list_user_api_keys( self, user_id: str, @@ -3692,7 +3965,6 @@ async def delete_user( 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. @@ -4537,6 +4809,277 @@ async def delete_user_authorized_application( request_options=request_options, ) + async def delete_waitlist_entry( + self, + id: str, + *, + request_options: RequestOptions | None = None, + ) -> None: + """Delete a waitlist entry + + Remove the entry from the waitlist. Its email address can join again unless a user with that email now exists in the environment. Deleting the entry does not revoke an invitation created by approving it — [revoke that invitation](https://workos.com/docs/reference/authkit/invitation/revoke) separately to withdraw access. + + Args: + id: The unique ID of the waitlist entry. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Raises: + NotFoundError: If the resource is not found (404). + AuthenticationError: If the API key is invalid (401). + RateLimitExceededError: If rate limited (429). + ServerError: If the server returns a 5xx error. + """ + await self._client.request( + method="delete", + path=("user_management", "waitlist_entries", str(id)), + request_options=request_options, + ) + + async def create_waitlist_entry_approve( + self, + id: str, + *, + request_options: RequestOptions | None = None, + ) -> WaitlistEntry: + """Approve a waitlist entry + + Approve a waitlist entry, create an invitation for its email address, and send the invitation email. Approving a denied entry reverses the denial. The approval is saved even when the invitation steps fail, so instead of retrying the approval, recover based on the outcome: + + - `200` — the entry is approved. If invitation creation failed, no invitation exists yet; [send](https://workos.com/docs/reference/authkit/invitation/send) one. + - `422` with code `invitation_email_not_sent` — the entry is approved and an invitation exists, but its email was not sent; [resend](https://workos.com/docs/reference/authkit/invitation/resend) it. + - `422` with code `invalid_state` — the entry was already approved. + + Args: + id: The unique ID of the waitlist entry. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + WaitlistEntry + + 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. + """ + return await self._client.request( + method="post", + path=("user_management", "waitlist_entries", str(id), "approve"), + model=WaitlistEntry, + request_options=request_options, + ) + + async def create_waitlist_entry_deny( + self, + id: str, + *, + request_options: RequestOptions | None = None, + ) -> WaitlistEntry: + """Deny a waitlist entry + + Deny a pending waitlist entry. Denying an entry that is not pending fails with the code `invalid_state`. A denial can be reversed by approving the entry. + + Args: + id: The unique ID of the waitlist entry. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + WaitlistEntry + + 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. + """ + return await self._client.request( + method="post", + path=("user_management", "waitlist_entries", str(id), "deny"), + model=WaitlistEntry, + request_options=request_options, + ) + + async def list_waitlists( + self, + *, + limit: int | None = None, + before: str | None = None, + after: str | None = None, + order: str | None = None, + request_options: RequestOptions | None = None, + ) -> AsyncPage[Waitlist]: + """List waitlists + + Get a list of the waitlists in the environment. All waitlists are returned in a single response — this endpoint is not paginated, so the `list_metadata` cursors are always `null`. + + Args: + limit: The limit. + before: The before. + after: The after. + order: The order. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + AsyncPage[Waitlist] + + Raises: + AuthenticationError: If the API key is invalid (401). + RateLimitExceededError: If rate limited (429). + ServerError: If the server returns a 5xx error. + """ + params = { + k: v + for k, v in { + "limit": limit, + "before": before, + "after": after, + "order": order, + }.items() + if v is not None + } + return await self._client.request_page( + method="get", + path=("user_management", "waitlists"), + model=Waitlist, + params=params, + request_options=request_options, + ) + + async def get_waitlist( + self, + id: str, + *, + request_options: RequestOptions | None = None, + ) -> Waitlist: + """Get a waitlist + + Get the details of an existing waitlist. + + Args: + id: The unique ID of the waitlist, or the literal `default` for the environment's default waitlist. The default waitlist is created when its first entry is added, so read requests for `default` return a `404` until then. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + Waitlist + + Raises: + NotFoundError: If the resource is not found (404). + AuthenticationError: If the API key is invalid (401). + RateLimitExceededError: If rate limited (429). + ServerError: If the server returns a 5xx error. + """ + return await self._client.request( + method="get", + path=("user_management", "waitlists", str(id)), + model=Waitlist, + request_options=request_options, + ) + + async def list_waitlist_entries( + self, + id: str, + *, + limit: int | None = None, + before: str | None = None, + after: str | None = None, + order: PaginationOrder | str | None = "desc", + state: UserManagementWaitlistsState | str | None = None, + email: str | None = None, + request_options: RequestOptions | None = None, + ) -> AsyncPage[WaitlistEntry]: + """List waitlist entries + + Get a list of entries on a waitlist matching the criteria specified. + + Args: + id: The unique ID of the waitlist, or the literal `default` for the environment's default waitlist. The default waitlist is created when its first entry is added, so read requests for `default` return a `404` until then. + limit: Upper limit on the number of objects to return, between `1` and `100`. Defaults to `10`. + before: An object ID that defines your place in the list. When the ID is not present, you are at the end of the list. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. + after: An object ID that defines your place in the list. When the ID is not present, you are at the end of the list. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to `desc`. + state: Filter waitlist entries by their state. + email: Filter waitlist entries by their exact email address. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + AsyncPage[WaitlistEntry] + + 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. + """ + params = { + k: v + for k, v in { + "limit": limit, + "before": before, + "after": after, + "order": enum_value(order) if order is not None else None, + "state": enum_value(state) if state is not None else None, + "email": email, + }.items() + if v is not None + } + return await self._client.request_page( + method="get", + path=("user_management", "waitlists", str(id), "entries"), + model=WaitlistEntry, + params=params, + request_options=request_options, + ) + + async def create_waitlist_entry( + self, + id: str, + *, + email: str, + additional_fields: dict[str, str] | None = None, + send_confirmation_email: bool | None = None, + request_options: RequestOptions | None = None, + ) -> WaitlistEntry: + """Create a waitlist entry + + Add an email address to the waitlist. Email addresses are normalized and unique per environment: a request for an email address already on the waitlist returns the existing entry unchanged (still with status `201`) and does not send another confirmation email. If a user with the email address already exists in the environment, the request fails with the code `user_already_exists`. + + Args: + id: The unique ID of the waitlist, or the literal `default` for the environment's default waitlist. Use `default` when adding the first entry — the default waitlist is created automatically. + email: The email address of the user joining the waitlist. + additional_fields: Object containing additional key/value pairs collected with the waitlist entry. Supports up to 50 string pairs, with keys up to 40 characters and values up to 600 characters. Values are user-provided — treat them as untrusted input when rendering or exporting. + send_confirmation_email: Whether to send the waitlist confirmation email to the user. Defaults to `false`. No email is sent when the waitlist confirmation email is disabled in the environment, even if `send_confirmation_email` is `true`. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + WaitlistEntry + + 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] = { + k: v + for k, v in { + "email": email, + "additional_fields": additional_fields, + "send_confirmation_email": send_confirmation_email, + }.items() + if v is not None + } + return await self._client.request( + method="post", + path=("user_management", "waitlists", str(id), "entries"), + body=body, + model=WaitlistEntry, + request_options=request_options, + ) + async def list_user_api_keys( self, user_id: str, diff --git a/src/workos/user_management/models/__init__.py b/src/workos/user_management/models/__init__.py index 141601d9..0559c4e3 100644 --- a/src/workos/user_management/models/__init__.py +++ b/src/workos/user_management/models/__init__.py @@ -37,6 +37,7 @@ from .create_user_invite_options import ( CreateUserInviteOptions as CreateUserInviteOptions, ) +from .create_waitlist_entry import CreateWaitlistEntry as CreateWaitlistEntry from .device_authorization_response import ( DeviceAuthorizationResponse as DeviceAuthorizationResponse, ) @@ -119,5 +120,10 @@ from .user_management_authentication_screen_hint import ( UserManagementAuthenticationScreenHint as UserManagementAuthenticationScreenHint, ) +from .user_management_waitlists_state import ( + UserManagementWaitlistsState as UserManagementWaitlistsState, +) from .verify_email_address import VerifyEmailAddress as VerifyEmailAddress from .verify_email_response import VerifyEmailResponse as VerifyEmailResponse +from .waitlist import Waitlist as Waitlist +from .waitlist_entry import WaitlistEntry as WaitlistEntry diff --git a/src/workos/user_management/models/create_password_reset_token.py b/src/workos/user_management/models/create_password_reset_token.py index 2a49b972..7e8f7791 100644 --- a/src/workos/user_management/models/create_password_reset_token.py +++ b/src/workos/user_management/models/create_password_reset_token.py @@ -1,32 +1,7 @@ # This file is auto-generated by oagen. Do not edit. -from __future__ import annotations +from typing import TypeAlias -from dataclasses import dataclass -from typing import Any +from workos.organizations.models.create_it_contact import CreateItContact -from workos._types import _raise_deserialize_error - - -@dataclass(slots=True) -class CreatePasswordResetToken: - """Create Password Reset Token model.""" - - email: str - """The email address of the user requesting a password reset.""" - - @classmethod - def from_dict(cls, data: dict[str, Any]) -> CreatePasswordResetToken: - """Deserialize from a dictionary.""" - try: - return cls( - email=data["email"], - ) - except (KeyError, ValueError) as e: - _raise_deserialize_error("CreatePasswordResetToken", e) - - def to_dict(self) -> dict[str, Any]: - """Serialize to a dictionary.""" - result: dict[str, Any] = {} - result["email"] = self.email - return result +CreatePasswordResetToken: TypeAlias = CreateItContact diff --git a/src/workos/user_management/models/create_waitlist_entry.py b/src/workos/user_management/models/create_waitlist_entry.py new file mode 100644 index 00000000..015f21ed --- /dev/null +++ b/src/workos/user_management/models/create_waitlist_entry.py @@ -0,0 +1,42 @@ +# 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 CreateWaitlistEntry: + """Create Waitlist Entry model.""" + + email: str + """The email address of the user joining the waitlist.""" + additional_fields: dict[str, str] | None = None + """Object containing additional key/value pairs collected with the waitlist entry. Supports up to 50 string pairs, with keys up to 40 characters and values up to 600 characters. Values are user-provided — treat them as untrusted input when rendering or exporting.""" + send_confirmation_email: bool | None = None + """Whether to send the waitlist confirmation email to the user. Defaults to `false`. No email is sent when the waitlist confirmation email is disabled in the environment, even if `send_confirmation_email` is `true`.""" + + @classmethod + def from_dict(cls, data: dict[str, Any]) -> CreateWaitlistEntry: + """Deserialize from a dictionary.""" + try: + return cls( + email=data["email"], + additional_fields=data.get("additional_fields"), + send_confirmation_email=data.get("send_confirmation_email"), + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error("CreateWaitlistEntry", e) + + def to_dict(self) -> dict[str, Any]: + """Serialize to a dictionary.""" + result: dict[str, Any] = {} + result["email"] = self.email + if self.additional_fields is not None: + result["additional_fields"] = self.additional_fields + if self.send_confirmation_email is not None: + result["send_confirmation_email"] = self.send_confirmation_email + return result diff --git a/src/workos/user_management/models/user_management_waitlists_state.py b/src/workos/user_management/models/user_management_waitlists_state.py new file mode 100644 index 00000000..48866c9e --- /dev/null +++ b/src/workos/user_management/models/user_management_waitlists_state.py @@ -0,0 +1,30 @@ +# This file is auto-generated by oagen. Do not edit. + +"""Enumeration of user management waitlists state values.""" + +from __future__ import annotations + +from enum import Enum +from typing import Literal, TypeAlias + + +class UserManagementWaitlistsState(str, Enum): + """Known values for UserManagementWaitlistsState.""" + + PENDING = "pending" + APPROVED = "approved" + DENIED = "denied" + + @classmethod + def _missing_(cls, value: object) -> UserManagementWaitlistsState | None: + if not isinstance(value, str): + return None + unknown = str.__new__(cls, value) + unknown._name_ = value.upper() + unknown._value_ = value + return unknown + + +UserManagementWaitlistsStateLiteral: TypeAlias = Literal[ + "pending", "approved", "denied" +] diff --git a/src/workos/user_management/models/waitlist.py b/src/workos/user_management/models/waitlist.py new file mode 100644 index 00000000..6e77cb71 --- /dev/null +++ b/src/workos/user_management/models/waitlist.py @@ -0,0 +1,45 @@ +# 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 + +from workos._types import _format_datetime, _parse_datetime, _raise_deserialize_error + + +@dataclass(slots=True) +class Waitlist: + """Waitlist model.""" + + object: Literal["waitlist"] + """Distinguishes the Waitlist object.""" + id: str + """The unique ID of the Waitlist.""" + created_at: datetime + """An ISO 8601 timestamp.""" + updated_at: datetime + """An ISO 8601 timestamp.""" + + @classmethod + def from_dict(cls, data: dict[str, Any]) -> Waitlist: + """Deserialize from a dictionary.""" + try: + return cls( + object=data.get("object", "waitlist"), + id=data["id"], + created_at=_parse_datetime(data["created_at"]), + updated_at=_parse_datetime(data["updated_at"]), + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error("Waitlist", 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["created_at"] = _format_datetime(self.created_at) + result["updated_at"] = _format_datetime(self.updated_at) + return result diff --git a/src/workos/user_management/models/waitlist_entry.py b/src/workos/user_management/models/waitlist_entry.py new file mode 100644 index 00000000..2fd60494 --- /dev/null +++ b/src/workos/user_management/models/waitlist_entry.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, Literal + +from workos._types import _format_datetime, _parse_datetime, _raise_deserialize_error +from workos.common.models.waitlist_entry_state import WaitlistEntryState + + +@dataclass(slots=True) +class WaitlistEntry: + """Waitlist Entry model.""" + + id: str + """The unique ID of the waitlist entry.""" + email: str + """The email address of the user on the waitlist.""" + state: WaitlistEntryState + """The state of the waitlist entry.""" + approved_at: datetime | None + """The timestamp when the entry was approved, or null if not yet approved.""" + created_at: datetime + """An ISO 8601 timestamp.""" + updated_at: datetime + """An ISO 8601 timestamp.""" + object: Literal["waitlist_entry"] + """Distinguishes the Waitlist Entry object.""" + additional_fields: dict[str, str] | None = None + """Additional fields submitted when the user joined the waitlist. Values are user-provided — treat them as untrusted input when rendering or exporting.""" + waitlist_id: str | None = None + """The unique ID of the waitlist the entry belongs to.""" + + @classmethod + def from_dict(cls, data: dict[str, Any]) -> WaitlistEntry: + """Deserialize from a dictionary.""" + try: + return cls( + id=data["id"], + email=data["email"], + state=WaitlistEntryState(data["state"]), + approved_at=_parse_datetime(_v_approved_at) + if (_v_approved_at := data["approved_at"]) is not None + else None, + created_at=_parse_datetime(data["created_at"]), + updated_at=_parse_datetime(data["updated_at"]), + object=data.get("object", "waitlist_entry"), + additional_fields=data.get("additional_fields"), + waitlist_id=data.get("waitlist_id"), + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error("WaitlistEntry", e) + + def to_dict(self) -> dict[str, Any]: + """Serialize to a dictionary.""" + result: dict[str, Any] = {} + result["id"] = self.id + result["email"] = self.email + result["state"] = ( + self.state.value if isinstance(self.state, Enum) else self.state + ) + if self.approved_at is not None: + result["approved_at"] = _format_datetime(self.approved_at) + else: + result["approved_at"] = None + result["created_at"] = _format_datetime(self.created_at) + result["updated_at"] = _format_datetime(self.updated_at) + result["object"] = self.object + if self.additional_fields is not None: + result["additional_fields"] = self.additional_fields + if self.waitlist_id is not None: + result["waitlist_id"] = self.waitlist_id + else: + result["waitlist_id"] = None + return result diff --git a/tests/test_user_management.py b/tests/test_user_management.py index 21109f21..3a76684d 100644 --- a/tests/test_user_management.py +++ b/tests/test_user_management.py @@ -45,7 +45,10 @@ UserCreateResponse, UserIdentitiesGetItem, UserInvite, + UserManagementWaitlistsState, VerifyEmailResponse, + Waitlist, + WaitlistEntry, ) @@ -280,7 +283,7 @@ def test_create_user(self, workos, httpx_mock): json=load_fixture("user_create_response.json"), ) result = workos.user_management.create_user( - email="test_email", password=PasswordPlaintext(password="test_value") + email="test_email", password=PasswordPlaintext(password="test_password") ) assert isinstance(result, UserCreateResponse) assert result.object == "user" @@ -322,7 +325,7 @@ def test_update_user(self, workos, httpx_mock): json=load_fixture("user.json"), ) result = workos.user_management.update_user( - "test_id", password=PasswordPlaintext(password="test_value") + "test_id", password=PasswordPlaintext(password="test_password") ) assert isinstance(result, User) assert result.object == "user" @@ -695,6 +698,119 @@ def test_delete_user_authorized_application(self, workos, httpx_mock): "/user_management/users/test_user_id/authorized_applications/test_application_id" ) + def test_delete_waitlist_entry(self, workos, httpx_mock): + httpx_mock.add_response(status_code=204) + result = workos.user_management.delete_waitlist_entry("test_id") + assert result is None + request = httpx_mock.get_request() + assert request.method == "DELETE" + assert request.url.path.endswith("/user_management/waitlist_entries/test_id") + + def test_create_waitlist_entry_approve(self, workos, httpx_mock): + httpx_mock.add_response( + json=load_fixture("waitlist_entry.json"), + ) + result = workos.user_management.create_waitlist_entry_approve("test_id") + assert isinstance(result, WaitlistEntry) + assert result.id == "wl_user_01E4ZCR3C56J083X43JQXF3JK5" + assert result.email == "marcelina.davis@example.com" + request = httpx_mock.get_request() + assert request.method == "POST" + assert request.url.path.endswith( + "/user_management/waitlist_entries/test_id/approve" + ) + + def test_create_waitlist_entry_deny(self, workos, httpx_mock): + httpx_mock.add_response( + json=load_fixture("waitlist_entry.json"), + ) + result = workos.user_management.create_waitlist_entry_deny("test_id") + assert isinstance(result, WaitlistEntry) + assert result.id == "wl_user_01E4ZCR3C56J083X43JQXF3JK5" + assert result.email == "marcelina.davis@example.com" + request = httpx_mock.get_request() + assert request.method == "POST" + assert request.url.path.endswith( + "/user_management/waitlist_entries/test_id/deny" + ) + + def test_list_waitlists(self, workos, httpx_mock): + httpx_mock.add_response( + json=load_fixture("list_waitlist.json"), + ) + page = workos.user_management.list_waitlists() + assert isinstance(page, SyncPage) + assert len(page.data) == 1 + assert isinstance(page.data[0], Waitlist) + + def test_list_waitlists_empty_page(self, workos, httpx_mock): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + page = workos.user_management.list_waitlists() + assert isinstance(page, SyncPage) + assert page.data == [] + + def test_get_waitlist(self, workos, httpx_mock): + httpx_mock.add_response( + json=load_fixture("waitlist.json"), + ) + result = workos.user_management.get_waitlist("test_id") + assert isinstance(result, Waitlist) + assert result.object == "waitlist" + assert result.id == "waitlist_01E4ZCR3C56J083X43JQXF3JK5" + request = httpx_mock.get_request() + assert request.method == "GET" + assert request.url.path.endswith("/user_management/waitlists/test_id") + + def test_list_waitlist_entries(self, workos, httpx_mock): + httpx_mock.add_response( + json=load_fixture("list_waitlist_entry.json"), + ) + page = workos.user_management.list_waitlist_entries("test_id") + assert isinstance(page, SyncPage) + assert len(page.data) == 1 + assert isinstance(page.data[0], WaitlistEntry) + + def test_list_waitlist_entries_empty_page(self, workos, httpx_mock): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + page = workos.user_management.list_waitlist_entries("test_id") + assert isinstance(page, SyncPage) + assert page.data == [] + + def test_list_waitlist_entries_encodes_query_params(self, workos, httpx_mock): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + workos.user_management.list_waitlist_entries( + "test_id", + limit=10, + before="cursor before", + after="cursor/after", + order=PaginationOrder("value_order"), + state=UserManagementWaitlistsState("pending"), + email="value email/test", + ) + request = httpx_mock.get_request() + assert request.url.params["limit"] == "10" + assert request.url.params["before"] == "cursor before" + assert request.url.params["after"] == "cursor/after" + assert request.url.params["order"] == "value_order" + assert request.url.params["state"] == "pending" + assert request.url.params["email"] == "value email/test" + + def test_create_waitlist_entry(self, workos, httpx_mock): + httpx_mock.add_response( + json=load_fixture("waitlist_entry.json"), + ) + result = workos.user_management.create_waitlist_entry( + "test_id", email="test_email" + ) + assert isinstance(result, WaitlistEntry) + assert result.id == "wl_user_01E4ZCR3C56J083X43JQXF3JK5" + assert result.email == "marcelina.davis@example.com" + request = httpx_mock.get_request() + assert request.method == "POST" + assert request.url.path.endswith("/user_management/waitlists/test_id/entries") + body = json.loads(request.content) + assert body["email"] == "test_email" + def test_list_user_api_keys(self, workos, httpx_mock): httpx_mock.add_response( json=load_fixture("list_user_api_key.json"), @@ -1162,7 +1278,7 @@ async def test_list_users_encodes_query_params(self, async_workos, httpx_mock): async def test_create_user(self, async_workos, httpx_mock): httpx_mock.add_response(json=load_fixture("user_create_response.json")) result = await async_workos.user_management.create_user( - email="test_email", password=PasswordPlaintext(password="test_value") + email="test_email", password=PasswordPlaintext(password="test_password") ) assert isinstance(result, UserCreateResponse) assert result.object == "user" @@ -1201,7 +1317,7 @@ async def test_get_user(self, async_workos, httpx_mock): async def test_update_user(self, async_workos, httpx_mock): httpx_mock.add_response(json=load_fixture("user.json")) result = await async_workos.user_management.update_user( - "test_id", password=PasswordPlaintext(password="test_value") + "test_id", password=PasswordPlaintext(password="test_password") ) assert isinstance(result, User) assert result.object == "user" @@ -1577,6 +1693,121 @@ async def test_delete_user_authorized_application(self, async_workos, httpx_mock "/user_management/users/test_user_id/authorized_applications/test_application_id" ) + @pytest.mark.asyncio + async def test_delete_waitlist_entry(self, async_workos, httpx_mock): + httpx_mock.add_response(status_code=204) + result = await async_workos.user_management.delete_waitlist_entry("test_id") + assert result is None + request = httpx_mock.get_request() + assert request.method == "DELETE" + assert request.url.path.endswith("/user_management/waitlist_entries/test_id") + + @pytest.mark.asyncio + async def test_create_waitlist_entry_approve(self, async_workos, httpx_mock): + httpx_mock.add_response(json=load_fixture("waitlist_entry.json")) + result = await async_workos.user_management.create_waitlist_entry_approve( + "test_id" + ) + assert isinstance(result, WaitlistEntry) + assert result.id == "wl_user_01E4ZCR3C56J083X43JQXF3JK5" + assert result.email == "marcelina.davis@example.com" + request = httpx_mock.get_request() + assert request.method == "POST" + assert request.url.path.endswith( + "/user_management/waitlist_entries/test_id/approve" + ) + + @pytest.mark.asyncio + async def test_create_waitlist_entry_deny(self, async_workos, httpx_mock): + httpx_mock.add_response(json=load_fixture("waitlist_entry.json")) + result = await async_workos.user_management.create_waitlist_entry_deny( + "test_id" + ) + assert isinstance(result, WaitlistEntry) + assert result.id == "wl_user_01E4ZCR3C56J083X43JQXF3JK5" + assert result.email == "marcelina.davis@example.com" + request = httpx_mock.get_request() + assert request.method == "POST" + assert request.url.path.endswith( + "/user_management/waitlist_entries/test_id/deny" + ) + + @pytest.mark.asyncio + async def test_list_waitlists(self, async_workos, httpx_mock): + httpx_mock.add_response(json=load_fixture("list_waitlist.json")) + page = await async_workos.user_management.list_waitlists() + assert isinstance(page, AsyncPage) + assert len(page.data) == 1 + assert isinstance(page.data[0], Waitlist) + + @pytest.mark.asyncio + async def test_list_waitlists_empty_page(self, async_workos, httpx_mock): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + page = await async_workos.user_management.list_waitlists() + assert isinstance(page, AsyncPage) + assert page.data == [] + + @pytest.mark.asyncio + async def test_get_waitlist(self, async_workos, httpx_mock): + httpx_mock.add_response(json=load_fixture("waitlist.json")) + result = await async_workos.user_management.get_waitlist("test_id") + assert isinstance(result, Waitlist) + assert result.object == "waitlist" + assert result.id == "waitlist_01E4ZCR3C56J083X43JQXF3JK5" + request = httpx_mock.get_request() + assert request.method == "GET" + assert request.url.path.endswith("/user_management/waitlists/test_id") + + @pytest.mark.asyncio + async def test_list_waitlist_entries(self, async_workos, httpx_mock): + httpx_mock.add_response(json=load_fixture("list_waitlist_entry.json")) + page = await async_workos.user_management.list_waitlist_entries("test_id") + assert isinstance(page, AsyncPage) + assert len(page.data) == 1 + assert isinstance(page.data[0], WaitlistEntry) + + @pytest.mark.asyncio + async def test_list_waitlist_entries_empty_page(self, async_workos, httpx_mock): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + page = await async_workos.user_management.list_waitlist_entries("test_id") + assert isinstance(page, AsyncPage) + assert page.data == [] + + @pytest.mark.asyncio + async def test_list_waitlist_entries_encodes_query_params( + self, async_workos, httpx_mock + ): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + await async_workos.user_management.list_waitlist_entries( + "test_id", + limit=10, + before="cursor before", + after="cursor/after", + order=PaginationOrder("value_order"), + state=UserManagementWaitlistsState("pending"), + email="value email/test", + ) + request = httpx_mock.get_request() + assert request.url.params["limit"] == "10" + assert request.url.params["before"] == "cursor before" + assert request.url.params["after"] == "cursor/after" + assert request.url.params["order"] == "value_order" + assert request.url.params["state"] == "pending" + assert request.url.params["email"] == "value email/test" + + @pytest.mark.asyncio + async def test_create_waitlist_entry(self, async_workos, httpx_mock): + httpx_mock.add_response(json=load_fixture("waitlist_entry.json")) + result = await async_workos.user_management.create_waitlist_entry( + "test_id", email="test_email" + ) + assert isinstance(result, WaitlistEntry) + assert result.id == "wl_user_01E4ZCR3C56J083X43JQXF3JK5" + assert result.email == "marcelina.davis@example.com" + request = httpx_mock.get_request() + assert request.method == "POST" + assert request.url.path.endswith("/user_management/waitlists/test_id/entries") + @pytest.mark.asyncio async def test_list_user_api_keys(self, async_workos, httpx_mock): httpx_mock.add_response(json=load_fixture("list_user_api_key.json")) diff --git a/tests/test_user_management_models_round_trip.py b/tests/test_user_management_models_round_trip.py index 518299b7..07a63f68 100644 --- a/tests/test_user_management_models_round_trip.py +++ b/tests/test_user_management_models_round_trip.py @@ -37,6 +37,8 @@ UserSessionsImpersonator, UserSessionsListItem, VerifyEmailResponse, + Waitlist, + WaitlistEntry, ) @@ -799,6 +801,103 @@ def test_device_authorization_response_omits_absent_optional_non_nullable_fields assert "verification_uri_complete" not in serialized assert "interval" not in serialized + def test_waitlist_round_trip(self): + data = load_fixture("waitlist.json") + instance = Waitlist.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = Waitlist.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_waitlist_minimal_payload(self): + data = { + "object": "waitlist", + "id": "waitlist_01E4ZCR3C56J083X43JQXF3JK5", + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + } + instance = Waitlist.from_dict(data) + serialized = instance.to_dict() + assert serialized["object"] == data["object"] + assert serialized["id"] == data["id"] + assert serialized["created_at"] == data["created_at"] + assert serialized["updated_at"] == data["updated_at"] + + def test_waitlist_entry_round_trip(self): + data = load_fixture("waitlist_entry.json") + instance = WaitlistEntry.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = WaitlistEntry.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_waitlist_entry_minimal_payload(self): + data = { + "id": "wl_user_01E4ZCR3C56J083X43JQXF3JK5", + "email": "marcelina.davis@example.com", + "state": "pending", + "approved_at": None, + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + "object": "waitlist_entry", + } + instance = WaitlistEntry.from_dict(data) + serialized = instance.to_dict() + assert serialized["id"] == data["id"] + assert serialized["email"] == data["email"] + assert serialized["state"] == data["state"] + assert serialized["approved_at"] == data["approved_at"] + assert serialized["created_at"] == data["created_at"] + assert serialized["updated_at"] == data["updated_at"] + assert serialized["object"] == data["object"] + + def test_waitlist_entry_omits_absent_optional_non_nullable_fields(self): + data = { + "id": "wl_user_01E4ZCR3C56J083X43JQXF3JK5", + "email": "marcelina.davis@example.com", + "state": "pending", + "approved_at": None, + "waitlist_id": "waitlist_01E4ZCR3C56J083X43JQXF3JK5", + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + "object": "waitlist_entry", + } + instance = WaitlistEntry.from_dict(data) + serialized = instance.to_dict() + assert "additional_fields" not in serialized + + def test_waitlist_entry_preserves_nullable_fields(self): + data = { + "id": "wl_user_01E4ZCR3C56J083X43JQXF3JK5", + "email": "marcelina.davis@example.com", + "state": "pending", + "approved_at": None, + "additional_fields": {"company": "Example Corp"}, + "waitlist_id": None, + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + "object": "waitlist_entry", + } + instance = WaitlistEntry.from_dict(data) + serialized = instance.to_dict() + assert serialized["approved_at"] is None + assert serialized["waitlist_id"] is None + + def test_waitlist_entry_round_trips_unknown_enum_values(self): + data = { + "id": "wl_user_01E4ZCR3C56J083X43JQXF3JK5", + "email": "marcelina.davis@example.com", + "state": "unexpected_waitlist_entry_state", + "approved_at": None, + "additional_fields": {"company": "Example Corp"}, + "waitlist_id": "waitlist_01E4ZCR3C56J083X43JQXF3JK5", + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + "object": "waitlist_entry", + } + instance = WaitlistEntry.from_dict(data) + assert instance.to_dict() == data + def test_jwks_response_round_trip(self): data = load_fixture("jwks_response.json") instance = JwksResponse.from_dict(data) From 50393fd531c30d514a01cb05f203ed4f3a74726c Mon Sep 17 00:00:00 2001 From: "workos-sdk-automation[bot]" <255426317+workos-sdk-automation[bot]@users.noreply.github.com> Date: Mon, 31 Aug 2026 20:17:46 +0000 Subject: [PATCH 2/3] chore(generated): regenerate shared files for UserManagement --- .last-synced-sha | 2 +- .oagen-manifest.json | 150 ++++++++++++++++++ src/workos/agents/models/__init__.py | 2 + src/workos/authorization/models/__init__.py | 1 - src/workos/common/__init__.py | 1 + src/workos/common/models/__init__.py | 1 + ...enticate_response_authentication_method.py | 6 - .../user_identities_get_item_provider.py | 6 - .../common/models/user_sessions_status.py | 5 +- .../common/models/waitlist_entry_state.py | 21 +++ src/workos/sso/__init__.py | 7 +- src/workos/sso/models/__init__.py | 2 +- tests/fixtures/create_waitlist_entry.json | 7 + tests/fixtures/list_waitlist.json | 14 ++ tests/fixtures/list_waitlist_entry.json | 21 +++ tests/fixtures/waitlist.json | 6 + tests/fixtures/waitlist_entry.json | 13 ++ 17 files changed, 247 insertions(+), 18 deletions(-) create mode 100644 src/workos/common/models/waitlist_entry_state.py create mode 100644 tests/fixtures/create_waitlist_entry.json create mode 100644 tests/fixtures/list_waitlist.json create mode 100644 tests/fixtures/list_waitlist_entry.json create mode 100644 tests/fixtures/waitlist.json create mode 100644 tests/fixtures/waitlist_entry.json diff --git a/.last-synced-sha b/.last-synced-sha index 066427ca..c971a578 100644 --- a/.last-synced-sha +++ b/.last-synced-sha @@ -1 +1 @@ -a07d8e7988d035c2d727787f18f64d71b4b89a84 +d57167a3c5beebe16cde40de046773b9861a6b47 diff --git a/.oagen-manifest.json b/.oagen-manifest.json index 08501acd..8847a531 100644 --- a/.oagen-manifest.json +++ b/.oagen-manifest.json @@ -523,6 +523,7 @@ "src/workos/common/models/vault_names_listed.py", "src/workos/common/models/vault_names_listed_data.py", "src/workos/common/models/vault_names_listed_data_actor_source.py", + "src/workos/common/models/waitlist_entry_state.py", "src/workos/common/models/waitlist_user.py", "src/workos/common/models/waitlist_user_approved.py", "src/workos/common/models/waitlist_user_created.py", @@ -701,6 +702,7 @@ "src/workos/user_management/models/create_user.py", "src/workos/user_management/models/create_user_api_key.py", "src/workos/user_management/models/create_user_invite_options.py", + "src/workos/user_management/models/create_waitlist_entry.py", "src/workos/user_management/models/device_authorization_response.py", "src/workos/user_management/models/device_code_session_authenticate_request.py", "src/workos/user_management/models/email_change.py", @@ -743,8 +745,11 @@ "src/workos/user_management/models/user_invite.py", "src/workos/user_management/models/user_management_authentication_provider.py", "src/workos/user_management/models/user_management_authentication_screen_hint.py", + "src/workos/user_management/models/user_management_waitlists_state.py", "src/workos/user_management/models/verify_email_address.py", "src/workos/user_management/models/verify_email_response.py", + "src/workos/user_management/models/waitlist.py", + "src/workos/user_management/models/waitlist_entry.py", "src/workos/vault/__init__.py", "src/workos/vault/_resource.py", "src/workos/vault/models/__init__.py", @@ -968,6 +973,7 @@ "tests/fixtures/create_user_api_key.json", "tests/fixtures/create_user_invite_options.json", "tests/fixtures/create_user_organization_membership.json", + "tests/fixtures/create_waitlist_entry.json", "tests/fixtures/create_webhook_endpoint.json", "tests/fixtures/custom_provider_definition.json", "tests/fixtures/data_integration.json", @@ -1132,6 +1138,8 @@ "tests/fixtures/list_user_organization_membership_base_list_data.json", "tests/fixtures/list_user_role_assignment.json", "tests/fixtures/list_user_sessions_list_item.json", + "tests/fixtures/list_waitlist.json", + "tests/fixtures/list_waitlist_entry.json", "tests/fixtures/list_webhook_endpoint.json", "tests/fixtures/magic_auth.json", "tests/fixtures/magic_auth_code_session_authenticate_request.json", @@ -1326,6 +1334,8 @@ "tests/fixtures/verify_email_address.json", "tests/fixtures/verify_email_response.json", "tests/fixtures/version_list_response.json", + "tests/fixtures/waitlist.json", + "tests/fixtures/waitlist_entry.json", "tests/fixtures/waitlist_user.json", "tests/fixtures/waitlist_user_approved.json", "tests/fixtures/waitlist_user_created.json", @@ -2229,6 +2239,146 @@ "PUT /data-integrations/{slug}/client-credentials": { "sdkMethod": "update_data_integration_client_credentials", "service": "pipes" + }, + "GET /agents/blueprints": { + "sdkMethod": "list_blueprints", + "service": "agents" + }, + "POST /agents/blueprints": { + "sdkMethod": "create_blueprint", + "service": "agents" + }, + "GET /agents/blueprints/{agent_blueprint_id}": { + "sdkMethod": "get_blueprint", + "service": "agents" + }, + "PATCH /agents/blueprints/{agent_blueprint_id}": { + "sdkMethod": "update_blueprint", + "service": "agents" + }, + "DELETE /agents/blueprints/{agent_blueprint_id}": { + "sdkMethod": "delete_blueprint", + "service": "agents" + }, + "POST /agents/blueprints/{agent_blueprint_id}/tokens": { + "sdkMethod": "create_blueprint_token", + "service": "agents" + }, + "GET /agents/instances": { + "sdkMethod": "list_instances", + "service": "agents" + }, + "GET /agents/instances/{agent_instance_id}": { + "sdkMethod": "get_instance", + "service": "agents" + }, + "DELETE /agents/instances/{agent_instance_id}": { + "sdkMethod": "delete_instance", + "service": "agents" + }, + "GET /agents/sessions": { + "sdkMethod": "list_sessions", + "service": "agents" + }, + "GET /agents/sessions/{agent_instance_session_id}": { + "sdkMethod": "get_session", + "service": "agents" + }, + "POST /agents/sessions/{agent_instance_session_id}/revoke": { + "sdkMethod": "revoke_session", + "service": "agents" + }, + "POST /connections": { + "sdkMethod": "create_connection", + "service": "sso" + }, + "GET /connections/{connectionId}/saml_idp_signing_certs": { + "sdkMethod": "list_connection_saml_idp_signing_certs", + "service": "sso" + }, + "POST /connections/{connectionId}/saml_idp_signing_certs": { + "sdkMethod": "create_connection_saml_idp_signing_cert", + "service": "sso" + }, + "DELETE /connections/{connectionId}/saml_idp_signing_certs/{certificateId}": { + "sdkMethod": "delete_connection_saml_idp_signing_cert", + "service": "sso" + }, + "GET /connections/{connectionId}/saml_sp_encryption_certs": { + "sdkMethod": "list_connection_saml_sp_encryption_certs", + "service": "sso" + }, + "POST /connections/{connectionId}/saml_sp_encryption_certs": { + "sdkMethod": "create_connection_saml_sp_encryption_cert", + "service": "sso" + }, + "DELETE /connections/{connectionId}/saml_sp_encryption_certs/{certificateId}": { + "sdkMethod": "delete_connection_saml_sp_encryption_cert", + "service": "sso" + }, + "GET /connections/{connectionId}/saml_sp_signing_cert": { + "sdkMethod": "list_connection_saml_sp_signing_cert", + "service": "sso" + }, + "POST /connections/{connectionId}/saml_sp_signing_cert": { + "sdkMethod": "create_connection_saml_sp_signing_cert", + "service": "sso" + }, + "DELETE /connections/{connectionId}/saml_sp_signing_cert/{certificateId}": { + "sdkMethod": "delete_connection_saml_sp_signing_cert", + "service": "sso" + }, + "PATCH /connections/{id}": { + "sdkMethod": "update_connection", + "service": "sso" + }, + "GET /organizations/{organization_id}/it_contacts": { + "sdkMethod": "list_it_contacts", + "service": "organizations" + }, + "POST /organizations/{organization_id}/it_contacts": { + "sdkMethod": "create_it_contact", + "service": "organizations" + }, + "DELETE /organizations/{organization_id}/it_contacts/{contact_id}": { + "sdkMethod": "delete_it_contact", + "service": "organizations" + }, + "POST /organizations/{organization_id}/it_contacts/{contact_id}/invite": { + "sdkMethod": "invite_it_contact", + "service": "organizations" + }, + "POST /organizations/{organization_id}/it_contacts/{contact_id}/revoke": { + "sdkMethod": "revoke_it_contact", + "service": "organizations" + }, + "DELETE /user_management/waitlist_entries/{id}": { + "sdkMethod": "delete_waitlist_entry", + "service": "user_management" + }, + "POST /user_management/waitlist_entries/{id}/approve": { + "sdkMethod": "create_waitlist_entry_approve", + "service": "user_management" + }, + "POST /user_management/waitlist_entries/{id}/deny": { + "sdkMethod": "create_waitlist_entry_deny", + "service": "user_management" + }, + "GET /user_management/waitlists": { + "sdkMethod": "list_waitlists", + "service": "user_management" + }, + "GET /user_management/waitlists/{id}": { + "sdkMethod": "get_waitlist", + "service": "user_management" + }, + "GET /user_management/waitlists/{id}/entries": { + "sdkMethod": "list_waitlist_entries", + "service": "user_management" + }, + "POST /user_management/waitlists/{id}/entries": { + "sdkMethod": "create_waitlist_entry", + "service": "user_management" } } } diff --git a/src/workos/agents/models/__init__.py b/src/workos/agents/models/__init__.py index a176f403..951f58ae 100644 --- a/src/workos/agents/models/__init__.py +++ b/src/workos/agents/models/__init__.py @@ -1,5 +1,7 @@ # This file is auto-generated by oagen. Do not edit. +from workos.common.models.pagination_order import PaginationOrder as PaginationOrder + from .agent_admin_link_claim_attempt_to_external_user_request import ( AgentAdminLinkClaimAttemptToExternalUserRequest as AgentAdminLinkClaimAttemptToExternalUserRequest, ) diff --git a/src/workos/authorization/models/__init__.py b/src/workos/authorization/models/__init__.py index 5251b450..3923b6cd 100644 --- a/src/workos/authorization/models/__init__.py +++ b/src/workos/authorization/models/__init__.py @@ -7,7 +7,6 @@ AuthorizationPermission as AuthorizationPermission, ) from workos.common.models.list_metadata import ListMetadata as ListMetadata -from workos.common.models.pagination_order import PaginationOrder as PaginationOrder from workos.common.models.slim_role import SlimRole as SlimRole from .assign_role import AssignRole as AssignRole diff --git a/src/workos/common/__init__.py b/src/workos/common/__init__.py index 563d82e2..c8b18243 100644 --- a/src/workos/common/__init__.py +++ b/src/workos/common/__init__.py @@ -537,6 +537,7 @@ from .models import VaultMetadataReadData as VaultMetadataReadData from .models import VaultNamesListed as VaultNamesListed from .models import VaultNamesListedData as VaultNamesListedData +from .models import WaitlistEntryState as WaitlistEntryState from .models import WaitlistUser as WaitlistUser from .models import WaitlistUserApproved as WaitlistUserApproved from .models import WaitlistUserCreated as WaitlistUserCreated diff --git a/src/workos/common/models/__init__.py b/src/workos/common/models/__init__.py index ae7ca22e..d85bbe6b 100644 --- a/src/workos/common/models/__init__.py +++ b/src/workos/common/models/__init__.py @@ -865,6 +865,7 @@ 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 diff --git a/src/workos/common/models/authenticate_response_authentication_method.py b/src/workos/common/models/authenticate_response_authentication_method.py index 53d66a1f..3eb1e9ad 100644 --- a/src/workos/common/models/authenticate_response_authentication_method.py +++ b/src/workos/common/models/authenticate_response_authentication_method.py @@ -17,13 +17,10 @@ class AuthenticateResponseAuthenticationMethod(str, Enum): APPLE_OAUTH = "AppleOAuth" BITBUCKET_OAUTH = "BitbucketOAuth" CROSS_APP_AUTH = "CrossAppAuth" - DISCORD_OAUTH = "DiscordOAuth" EXTERNAL_AUTH = "ExternalAuth" GIT_HUB_OAUTH = "GitHubOAuth" GIT_LAB_OAUTH = "GitLabOAuth" GOOGLE_OAUTH = "GoogleOAuth" - GROK_OAUTH = "GrokOAuth" - XO_AUTH = "XOAuth" INTUIT_OAUTH = "IntuitOAuth" LINKED_IN_OAUTH = "LinkedInOAuth" MICROSOFT_OAUTH = "MicrosoftOAuth" @@ -55,13 +52,10 @@ def _missing_( "AppleOAuth", "BitbucketOAuth", "CrossAppAuth", - "DiscordOAuth", "ExternalAuth", "GitHubOAuth", "GitLabOAuth", "GoogleOAuth", - "GrokOAuth", - "XOAuth", "IntuitOAuth", "LinkedInOAuth", "MicrosoftOAuth", diff --git a/src/workos/common/models/user_identities_get_item_provider.py b/src/workos/common/models/user_identities_get_item_provider.py index 1e512d8f..373a49aa 100644 --- a/src/workos/common/models/user_identities_get_item_provider.py +++ b/src/workos/common/models/user_identities_get_item_provider.py @@ -13,12 +13,9 @@ class UserIdentitiesGetItemProvider(str, Enum): APPLE_OAUTH = "AppleOAuth" BITBUCKET_OAUTH = "BitbucketOAuth" - DISCORD_OAUTH = "DiscordOAuth" GITHUB_OAUTH = "GithubOAuth" GIT_LAB_OAUTH = "GitLabOAuth" GOOGLE_OAUTH = "GoogleOAuth" - GROK_OAUTH = "GrokOAuth" - XO_AUTH = "XOAuth" INTUIT_OAUTH = "IntuitOAuth" LINKED_IN_OAUTH = "LinkedInOAuth" MICROSOFT_OAUTH = "MicrosoftOAuth" @@ -41,12 +38,9 @@ def _missing_(cls, value: object) -> UserIdentitiesGetItemProvider | None: UserIdentitiesGetItemProviderLiteral: TypeAlias = Literal[ "AppleOAuth", "BitbucketOAuth", - "DiscordOAuth", "GithubOAuth", "GitLabOAuth", "GoogleOAuth", - "GrokOAuth", - "XOAuth", "IntuitOAuth", "LinkedInOAuth", "MicrosoftOAuth", diff --git a/src/workos/common/models/user_sessions_status.py b/src/workos/common/models/user_sessions_status.py index 3c075ba3..4ae023f9 100644 --- a/src/workos/common/models/user_sessions_status.py +++ b/src/workos/common/models/user_sessions_status.py @@ -1,7 +1,8 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from .session_created_data_status import SessionCreatedDataStatus -UserSessionsStatus: TypeAlias = SessionCreatedDataStatus +from .agent_instance_session_status import AgentInstanceSessionStatus + +UserSessionsStatus: TypeAlias = AgentInstanceSessionStatus __all__ = ["UserSessionsStatus"] diff --git a/src/workos/common/models/waitlist_entry_state.py b/src/workos/common/models/waitlist_entry_state.py new file mode 100644 index 00000000..f71ae751 --- /dev/null +++ b/src/workos/common/models/waitlist_entry_state.py @@ -0,0 +1,21 @@ +# This file is auto-generated by oagen. Do not edit. + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from workos.user_management.models.user_management_waitlists_state import ( + UserManagementWaitlistsState as WaitlistEntryState, + ) +else: + + def __getattr__(name: str): + if name == "WaitlistEntryState": + from workos.user_management.models.user_management_waitlists_state import ( + UserManagementWaitlistsState, + ) + + return UserManagementWaitlistsState + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + +__all__ = ["WaitlistEntryState"] diff --git a/src/workos/sso/__init__.py b/src/workos/sso/__init__.py index be562a2a..66fa7567 100644 --- a/src/workos/sso/__init__.py +++ b/src/workos/sso/__init__.py @@ -1,4 +1,9 @@ # This file is auto-generated by oagen. Do not edit. -from ._resource import SSO as SSO, AsyncSSO as AsyncSSO +from ._resource import SSO as SSO +from ._resource import AsyncSSO as AsyncSSO +from ._resource import CreateProtocolOptionsOIDC as CreateProtocolOptionsOIDC +from ._resource import CreateProtocolOptionsSAML as CreateProtocolOptionsSAML +from ._resource import PatchProtocolOptionsOIDC as PatchProtocolOptionsOIDC +from ._resource import PatchProtocolOptionsSAML as PatchProtocolOptionsSAML from .models import * diff --git a/src/workos/sso/models/__init__.py b/src/workos/sso/models/__init__.py index 8fd3e22e..be257283 100644 --- a/src/workos/sso/models/__init__.py +++ b/src/workos/sso/models/__init__.py @@ -20,4 +20,4 @@ from .sso_token_response_oauth_token import ( SSOTokenResponseOAuthToken as SSOTokenResponseOAuthToken, ) -from .token_query import TokenQuery as TokenQuery +from .token_query import * diff --git a/tests/fixtures/create_waitlist_entry.json b/tests/fixtures/create_waitlist_entry.json new file mode 100644 index 00000000..c53e1ff7 --- /dev/null +++ b/tests/fixtures/create_waitlist_entry.json @@ -0,0 +1,7 @@ +{ + "email": "marcelina.davis@example.com", + "additional_fields": { + "company": "Example Corp" + }, + "send_confirmation_email": false +} diff --git a/tests/fixtures/list_waitlist.json b/tests/fixtures/list_waitlist.json new file mode 100644 index 00000000..fe1e33af --- /dev/null +++ b/tests/fixtures/list_waitlist.json @@ -0,0 +1,14 @@ +{ + "data": [ + { + "object": "waitlist", + "id": "waitlist_01E4ZCR3C56J083X43JQXF3JK5", + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z" + } + ], + "list_metadata": { + "before": null, + "after": null + } +} diff --git a/tests/fixtures/list_waitlist_entry.json b/tests/fixtures/list_waitlist_entry.json new file mode 100644 index 00000000..d124178f --- /dev/null +++ b/tests/fixtures/list_waitlist_entry.json @@ -0,0 +1,21 @@ +{ + "data": [ + { + "id": "wl_user_01E4ZCR3C56J083X43JQXF3JK5", + "email": "marcelina.davis@example.com", + "state": "pending", + "approved_at": null, + "additional_fields": { + "company": "Example Corp" + }, + "waitlist_id": "waitlist_01E4ZCR3C56J083X43JQXF3JK5", + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + "object": "waitlist_entry" + } + ], + "list_metadata": { + "before": null, + "after": null + } +} diff --git a/tests/fixtures/waitlist.json b/tests/fixtures/waitlist.json new file mode 100644 index 00000000..1eb2c771 --- /dev/null +++ b/tests/fixtures/waitlist.json @@ -0,0 +1,6 @@ +{ + "object": "waitlist", + "id": "waitlist_01E4ZCR3C56J083X43JQXF3JK5", + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z" +} diff --git a/tests/fixtures/waitlist_entry.json b/tests/fixtures/waitlist_entry.json new file mode 100644 index 00000000..96887183 --- /dev/null +++ b/tests/fixtures/waitlist_entry.json @@ -0,0 +1,13 @@ +{ + "id": "wl_user_01E4ZCR3C56J083X43JQXF3JK5", + "email": "marcelina.davis@example.com", + "state": "pending", + "approved_at": null, + "additional_fields": { + "company": "Example Corp" + }, + "waitlist_id": "waitlist_01E4ZCR3C56J083X43JQXF3JK5", + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + "object": "waitlist_entry" +} From c7cfa570ca959c93c278c5f5e6ba7e87d6aa9b4c Mon Sep 17 00:00:00 2001 From: "workos-sdk-automation[bot]" <255426317+workos-sdk-automation[bot]@users.noreply.github.com> Date: Mon, 31 Aug 2026 20:17:55 +0000 Subject: [PATCH 3/3] chore(generated): add release notes fragment --- ...70a517bb095451981b20916045962cafa14d81b.md | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .changelog-pending/2026-08-31T20-17-55-a70a517bb095451981b20916045962cafa14d81b.md diff --git a/.changelog-pending/2026-08-31T20-17-55-a70a517bb095451981b20916045962cafa14d81b.md b/.changelog-pending/2026-08-31T20-17-55-a70a517bb095451981b20916045962cafa14d81b.md new file mode 100644 index 00000000..49e5ff6c --- /dev/null +++ b/.changelog-pending/2026-08-31T20-17-55-a70a517bb095451981b20916045962cafa14d81b.md @@ -0,0 +1,20 @@ +* [#717](https://github.com/workos/workos-python/pull/717) feat(generated)!: regenerate from spec (1 change) + + **Features** + * **[user_management](https://workos.com/docs/reference/authkit/user)**: + * Added model `CreateWaitlistEntry` + * Added model `Waitlist` + * Added model `WaitlistEntry` + * Added enum `WaitlistEntryState` + * Added enum `UserManagementWaitlistsState` + * Added service `UserManagementWaitlists` + + **Fixes** + * **[user_management](https://workos.com/docs/reference/authkit/user)**: + * Removed `DiscordOAuth` from `AuthenticateResponseAuthenticationMethod` + * Removed `GrokOAuth` from `AuthenticateResponseAuthenticationMethod` + * Removed `XOAuth` from `AuthenticateResponseAuthenticationMethod` + * Removed `DiscordOAuth` from `UserIdentitiesGetItemProvider` + * Removed `GrokOAuth` from `UserIdentitiesGetItemProvider` + * Removed `XOAuth` from `UserIdentitiesGetItemProvider` + * Changed errors for endpoint `DELETE /user_management/users/{id}`