Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
* [#683](https://github.com/workos/workos-python/pull/683) fix(generated): regenerate from spec

**⚠️ Breaking**
* **[user_management](https://workos.com/docs/reference/authkit/user)**:
* Removed model `SessionReauthenticated`
* Removed model `SessionReauthenticatedData`
* Removed model `SessionReauthenticatedDataImpersonator`
* Removed enum `SessionReauthenticatedDataAuthMethod`
* Removed enum `SessionReauthenticatedDataStatus`

**Features**
* **[webhooks](https://workos.com/docs/reference/webhooks)**:
* Added `agent.registration.created` to `CreateWebhookEndpointEvents`
* Added `agent.registration.claim.attempt.created` to `CreateWebhookEndpointEvents`
* Added `agent.registration.claim.completed` to `CreateWebhookEndpointEvents`
* Added `agent.registration.credential.issued` to `CreateWebhookEndpointEvents`
* Added `agent.registration.organization.switched` to `CreateWebhookEndpointEvents`
* Added `authentication.reauthentication_succeeded` to `CreateWebhookEndpointEvents`
* Added `agent.registration.created` to `UpdateWebhookEndpointEvents`
* Added `agent.registration.claim.attempt.created` to `UpdateWebhookEndpointEvents`
* Added `agent.registration.claim.completed` to `UpdateWebhookEndpointEvents`
* Added `agent.registration.credential.issued` to `UpdateWebhookEndpointEvents`
* Added `agent.registration.organization.switched` to `UpdateWebhookEndpointEvents`
* Added `authentication.reauthentication_succeeded` to `UpdateWebhookEndpointEvents`
* **[webhooks](https://workos.com/docs/reference/webhooks)**:
* Added `session.reauthenticated` to `CreateWebhookEndpointEvents`
* Added `session.reauthenticated` to `UpdateWebhookEndpointEvents`
* **[webhooks](https://workos.com/docs/reference/webhooks)**:
* Added `pipes.connected_account.connection_failed` to `CreateWebhookEndpointEvents`
* Added `pipes.connected_account.connection_failed` to `UpdateWebhookEndpointEvents`
* **[user_management](https://workos.com/docs/reference/authkit/user)**:
* Added model `UserRoleAssignmentSource`
* Added `source` to `UserRoleAssignment`
* Added enum `UserRoleAssignmentSourceType`
* Added parameter `UserManagementAuthentication.authorize.max_age`
* Added endpoint `GET /user_management/cors_origins`
* Added endpoint `GET /user_management/redirect_uris`
* **[audit_logs](https://workos.com/docs/reference/audit-logs)**:
* Changed the format of `AuditLogExportCreation.range_start`
* Changed the format of `AuditLogExportCreation.range_end`
* **[audit_logs](https://workos.com/docs/reference/audit-logs)**:
* Added `expired` to `AuditLogExportState`

**Fixes**
* **[admin_portal](https://workos.com/docs/reference/admin-portal)**:
* Removed `intent_options` from `GenerateLink`
* **[webhooks](https://workos.com/docs/reference/webhooks)**:
* Removed `session.reauthenticated` from `CreateWebhookEndpointEvents`
* Removed `session.reauthenticated` from `UpdateWebhookEndpointEvents`
16 changes: 15 additions & 1 deletion .oagen-manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"version": 2,
"language": "python",
"generatedAt": "2026-07-01T16:28:45.662Z",
"generatedAt": "2026-07-01T18:19:12.330Z",
"files": [
"src/workos/_client.py",
"src/workos/admin_portal/__init__.py",
Expand Down Expand Up @@ -73,6 +73,7 @@
"src/workos/authorization/models/update_role.py",
"src/workos/authorization/models/user_role_assignment.py",
"src/workos/authorization/models/user_role_assignment_resource.py",
"src/workos/authorization/models/user_role_assignment_source.py",
"src/workos/client_api/__init__.py",
"src/workos/client_api/_resource.py",
"src/workos/client_api/models/__init__.py",
Expand Down Expand Up @@ -428,6 +429,7 @@
"src/workos/common/models/user_organization_membership_base_list_data.py",
"src/workos/common/models/user_organization_membership_base_list_data_status.py",
"src/workos/common/models/user_organization_membership_status.py",
"src/workos/common/models/user_role_assignment_source_type.py",
"src/workos/common/models/user_sessions_auth_method.py",
"src/workos/common/models/user_sessions_impersonator.py",
"src/workos/common/models/user_sessions_list_item.py",
Expand Down Expand Up @@ -992,6 +994,7 @@
"tests/fixtures/list_authorized_connect_application_list_data.json",
"tests/fixtures/list_connect_application.json",
"tests/fixtures/list_connection.json",
"tests/fixtures/list_cors_origin_response.json",
"tests/fixtures/list_directory.json",
"tests/fixtures/list_directory_group.json",
"tests/fixtures/list_directory_user_with_groups.json",
Expand All @@ -1003,6 +1006,7 @@
"tests/fixtures/list_object_summary.json",
"tests/fixtures/list_organization.json",
"tests/fixtures/list_organization_api_key.json",
"tests/fixtures/list_redirect_uri.json",
"tests/fixtures/list_user.json",
"tests/fixtures/list_user_api_key.json",
"tests/fixtures/list_user_invite.json",
Expand Down Expand Up @@ -1159,6 +1163,7 @@
"tests/fixtures/user_organization_membership_base_list_data.json",
"tests/fixtures/user_role_assignment.json",
"tests/fixtures/user_role_assignment_resource.json",
"tests/fixtures/user_role_assignment_source.json",
"tests/fixtures/user_sessions_impersonator.json",
"tests/fixtures/user_sessions_list_item.json",
"tests/fixtures/user_updated.json",
Expand Down Expand Up @@ -1197,27 +1202,36 @@
"tests/fixtures/widget_session_token.json",
"tests/fixtures/widget_session_token_response.json",
"tests/test_admin_portal.py",
"tests/test_admin_portal_models_round_trip.py",
"tests/test_api_keys.py",
"tests/test_audit_logs.py",
"tests/test_audit_logs_models_round_trip.py",
"tests/test_authorization.py",
"tests/test_authorization_models_round_trip.py",
"tests/test_client_api.py",
"tests/test_common_models_round_trip.py",
"tests/test_connect.py",
"tests/test_connect_models_round_trip.py",
"tests/test_directory_sync.py",
"tests/test_events.py",
"tests/test_feature_flags.py",
"tests/test_groups.py",
"tests/test_groups_models_round_trip.py",
"tests/test_models_round_trip.py",
"tests/test_multi_factor_auth.py",
"tests/test_organization_domains.py",
"tests/test_organization_membership.py",
"tests/test_organizations.py",
"tests/test_organizations_models_round_trip.py",
"tests/test_pipes.py",
"tests/test_pipes_provider.py",
"tests/test_radar.py",
"tests/test_sso.py",
"tests/test_user_management.py",
"tests/test_user_management_models_round_trip.py",
"tests/test_vault.py",
"tests/test_webhooks.py",
"tests/test_webhooks_models_round_trip.py",
"tests/test_widgets.py"
],
"operations": {
Expand Down
12 changes: 1 addition & 11 deletions src/workos/admin_portal/_resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from .._client import AsyncWorkOSClient, WorkOSClient

from .._types import RequestOptions, enum_value
from .models import IntentOptions, PortalLinkResponse
from .models import PortalLinkResponse

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚩 Removal of intent_options is a breaking API change for existing callers

The intent_options parameter was removed from both AdminPortal.generate_link and AsyncAdminPortal.generate_link (src/workos/admin_portal/_resource.py:11,28-29), and from the GenerateLink model. The IntentOptions, SSOIntentOptions, and DomainVerificationIntentOptions classes still exist in src/workos/admin_portal/models/ and are still exported from __init__.py via wildcard imports. Any existing callers passing intent_options=... will get a TypeError at runtime. Since this is auto-generated and reflects an API spec change, it's presumably intentional, but it's a breaking change worth noting.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

from workos.common.models.generate_link_intent import GenerateLinkIntent


Expand All @@ -25,7 +25,6 @@ def generate_link(
return_url: Optional[str] = None,
success_url: Optional[str] = None,
intent: Optional[Union[GenerateLinkIntent, str]] = None,
intent_options: Optional[IntentOptions] = None,
it_contact_emails: Optional[List[str]] = None,
request_options: Optional[RequestOptions] = None,
) -> PortalLinkResponse:
Expand All @@ -45,7 +44,6 @@ def generate_link(
- `domain_verification` - Launch Admin Portal for Domain Verification
- `certificate_renewal` - Launch Admin Portal for renewing SAML Certificates
- `bring_your_own_key` - Launch Admin Portal for configuring Bring Your Own Key
intent_options: Options to configure the Admin Portal based on the intent.
it_contact_emails: The email addresses of the IT contacts to grant access to the Admin Portal for the given organization. Accepts up to 20 emails.
request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override.

Expand All @@ -68,9 +66,6 @@ def generate_link(
"success_url": success_url,
"organization": organization,
"intent": enum_value(intent) if intent is not None else None,
"intent_options": intent_options.to_dict()
if intent_options is not None
else None,
"it_contact_emails": it_contact_emails,
}.items()
if v is not None
Expand All @@ -97,7 +92,6 @@ async def generate_link(
return_url: Optional[str] = None,
success_url: Optional[str] = None,
intent: Optional[Union[GenerateLinkIntent, str]] = None,
intent_options: Optional[IntentOptions] = None,
it_contact_emails: Optional[List[str]] = None,
request_options: Optional[RequestOptions] = None,
) -> PortalLinkResponse:
Expand All @@ -117,7 +111,6 @@ async def generate_link(
- `domain_verification` - Launch Admin Portal for Domain Verification
- `certificate_renewal` - Launch Admin Portal for renewing SAML Certificates
- `bring_your_own_key` - Launch Admin Portal for configuring Bring Your Own Key
intent_options: Options to configure the Admin Portal based on the intent.
it_contact_emails: The email addresses of the IT contacts to grant access to the Admin Portal for the given organization. Accepts up to 20 emails.
request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override.

Expand All @@ -140,9 +133,6 @@ async def generate_link(
"success_url": success_url,
"organization": organization,
"intent": enum_value(intent) if intent is not None else None,
"intent_options": intent_options.to_dict()
if intent_options is not None
else None,
"it_contact_emails": it_contact_emails,
}.items()
if v is not None
Expand Down
12 changes: 0 additions & 12 deletions src/workos/admin_portal/models/generate_link.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,8 @@

from dataclasses import dataclass
from enum import Enum
from typing import cast
from typing import Any, Dict, List, Optional
from workos._types import _raise_deserialize_error

from .intent_options import IntentOptions
from workos.common.models.generate_link_intent import GenerateLinkIntent


Expand All @@ -32,8 +29,6 @@ class GenerateLink:
- `domain_verification` - Launch Admin Portal for Domain Verification
- `certificate_renewal` - Launch Admin Portal for renewing SAML Certificates
- `bring_your_own_key` - Launch Admin Portal for configuring Bring Your Own Key"""
intent_options: Optional["IntentOptions"] = None
"""Options to configure the Admin Portal based on the intent."""
it_contact_emails: Optional[List[str]] = None
"""The email addresses of the IT contacts to grant access to the Admin Portal for the given organization. Accepts up to 20 emails."""

Expand All @@ -48,11 +43,6 @@ def from_dict(cls, data: Dict[str, Any]) -> "GenerateLink":
intent=GenerateLinkIntent(_v_intent)
if (_v_intent := data.get("intent")) is not None
else None,
intent_options=IntentOptions.from_dict(
cast(Dict[str, Any], _v_intent_options)
)
if (_v_intent_options := data.get("intent_options")) is not None
else None,
it_contact_emails=data.get("it_contact_emails"),
)
except (KeyError, ValueError) as e:
Expand All @@ -70,8 +60,6 @@ def to_dict(self) -> Dict[str, Any]:
result["intent"] = (
self.intent.value if isinstance(self.intent, Enum) else self.intent
)
if self.intent_options is not None:
result["intent_options"] = self.intent_options.to_dict()
if self.it_contact_emails is not None:
result["it_contact_emails"] = self.it_contact_emails
return result
2 changes: 1 addition & 1 deletion src/workos/audit_logs/models/audit_log_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class AuditLogExport:
id: str
"""The unique ID of the Audit Log Export."""
state: "AuditLogExportState"
"""The state of the export. Possible values: pending, ready, error."""
"""The state of the export. Possible values: pending, ready, error, expired."""
created_at: datetime
"""An ISO 8601 timestamp."""
updated_at: datetime
Expand Down
14 changes: 8 additions & 6 deletions src/workos/audit_logs/models/audit_log_export_creation.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
from __future__ import annotations

from dataclasses import dataclass
from datetime import datetime
from typing import Any, Dict, List, Optional
from workos._types import _raise_deserialize_error
from workos._types import _format_datetime, _parse_datetime


@dataclass(slots=True)
Expand All @@ -13,9 +15,9 @@ class AuditLogExportCreation:

organization_id: str
"""The unique ID of the Organization."""
range_start: str
range_start: datetime
"""ISO-8601 value for start of the export range."""
range_end: str
range_end: datetime
"""ISO-8601 value for end of the export range."""
actions: Optional[List[str]] = None
"""List of actions to filter against."""
Expand All @@ -36,8 +38,8 @@ def from_dict(cls, data: Dict[str, Any]) -> "AuditLogExportCreation":
try:
return cls(
organization_id=data["organization_id"],
range_start=data["range_start"],
range_end=data["range_end"],
range_start=_parse_datetime(data["range_start"]),
range_end=_parse_datetime(data["range_end"]),
actions=data.get("actions"),
actors=data.get("actors"),
actor_names=data.get("actor_names"),
Expand All @@ -51,8 +53,8 @@ def to_dict(self) -> Dict[str, Any]:
"""Serialize to a dictionary."""
result: Dict[str, Any] = {}
result["organization_id"] = self.organization_id
result["range_start"] = self.range_start
result["range_end"] = self.range_end
result["range_start"] = _format_datetime(self.range_start)
result["range_end"] = _format_datetime(self.range_end)
Comment on lines +56 to +57

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Preserve string dates
range_start and range_end were public ISO-8601 string fields before this regeneration, but to_dict() now unconditionally calls _format_datetime(). Existing callers that construct AuditLogExportCreation(range_start="2022-...Z", range_end="2022-...Z") now hit AttributeError: 'str' object has no attribute 'isoformat' during serialization, even though the resource method and docs still accept strings for export ranges.

Artifacts

Repro: minimal script constructing AuditLogExportCreation with string dates and calling to_dict

  • Contains supporting evidence from the run (text/x-python; charset=utf-8).

Repro: command output and full traceback showing AttributeError from to_dict/_format_datetime

  • Keeps the command output available without making the summary code-heavy.

View artifacts

T-Rex Ran code and verified through T-Rex

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/workos/audit_logs/models/audit_log_export_creation.py
Line: 56-57

Comment:
**Preserve string dates**
`range_start` and `range_end` were public ISO-8601 string fields before this regeneration, but `to_dict()` now unconditionally calls `_format_datetime()`. Existing callers that construct `AuditLogExportCreation(range_start="2022-...Z", range_end="2022-...Z")` now hit `AttributeError: 'str' object has no attribute 'isoformat'` during serialization, even though the resource method and docs still accept strings for export ranges.

How can I resolve this? If you propose a fix, please make it concise.

if self.actions is not None:
result["actions"] = self.actions
if self.actors is not None:
Expand Down
3 changes: 3 additions & 0 deletions src/workos/authorization/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,6 @@
from .user_role_assignment_resource import (
UserRoleAssignmentResource as UserRoleAssignmentResource,
)
from .user_role_assignment_source import (
UserRoleAssignmentSource as UserRoleAssignmentSource,
)
7 changes: 7 additions & 0 deletions src/workos/authorization/models/user_role_assignment.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

from workos.common.models.slim_role import SlimRole
from .user_role_assignment_resource import UserRoleAssignmentResource
from .user_role_assignment_source import UserRoleAssignmentSource


@dataclass(slots=True)
Expand All @@ -27,6 +28,8 @@ class UserRoleAssignment:
"""The role included in the assignment."""
resource: "UserRoleAssignmentResource"
"""The resource the role is assigned on."""
source: "UserRoleAssignmentSource"
"""The origin of the role assignment."""
created_at: datetime
"""An ISO 8601 timestamp."""
updated_at: datetime
Expand All @@ -44,6 +47,9 @@ def from_dict(cls, data: Dict[str, Any]) -> "UserRoleAssignment":
resource=UserRoleAssignmentResource.from_dict(
cast(Dict[str, Any], data["resource"])
),
source=UserRoleAssignmentSource.from_dict(
cast(Dict[str, Any], data["source"])
),
created_at=_parse_datetime(data["created_at"]),
updated_at=_parse_datetime(data["updated_at"]),
)
Expand All @@ -58,6 +64,7 @@ def to_dict(self) -> Dict[str, Any]:
result["organization_membership_id"] = self.organization_membership_id
result["role"] = self.role.to_dict()
result["resource"] = self.resource.to_dict()
result["source"] = self.source.to_dict()
result["created_at"] = _format_datetime(self.created_at)
result["updated_at"] = _format_datetime(self.updated_at)
return result
42 changes: 42 additions & 0 deletions src/workos/authorization/models/user_role_assignment_source.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# 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, Dict, Optional
from workos._types import _raise_deserialize_error
from workos.common.models.user_role_assignment_source_type import (
UserRoleAssignmentSourceType,
)


@dataclass(slots=True)
class UserRoleAssignmentSource:
"""The origin of the role assignment."""

type: "UserRoleAssignmentSourceType"
"""Whether the role was assigned directly or derived from a group."""
group_role_assignment_id: Optional[str]
"""The ID of the group role assignment the role was derived from, or null if direct."""

@classmethod
def from_dict(cls, data: Dict[str, Any]) -> "UserRoleAssignmentSource":
"""Deserialize from a dictionary."""
try:
return cls(
type=UserRoleAssignmentSourceType(data["type"]),
group_role_assignment_id=data["group_role_assignment_id"],
)
except (KeyError, ValueError) as e:
_raise_deserialize_error("UserRoleAssignmentSource", e)

def to_dict(self) -> Dict[str, Any]:
"""Serialize to a dictionary."""
result: Dict[str, Any] = {}
result["type"] = self.type.value if isinstance(self.type, Enum) else self.type
if self.group_role_assignment_id is not None:
result["group_role_assignment_id"] = self.group_role_assignment_id
else:
result["group_role_assignment_id"] = None
return result
1 change: 1 addition & 0 deletions src/workos/common/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,7 @@
UserOrganizationMembershipBaseListDataStatus as UserOrganizationMembershipBaseListDataStatus,
)
from .models import UserOrganizationMembershipStatus as UserOrganizationMembershipStatus
from .models import UserRoleAssignmentSourceType as UserRoleAssignmentSourceType
from .models import UserSessionsAuthMethod as UserSessionsAuthMethod
from .models import UserSessionsImpersonator as UserSessionsImpersonator
from .models import UserSessionsListItem as UserSessionsListItem
Expand Down
3 changes: 3 additions & 0 deletions src/workos/common/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,9 @@
from .user_organization_membership_status import (
UserOrganizationMembershipStatus as UserOrganizationMembershipStatus,
)
from .user_role_assignment_source_type import (
UserRoleAssignmentSourceType as UserRoleAssignmentSourceType,
)
from .user_sessions_auth_method import UserSessionsAuthMethod as UserSessionsAuthMethod
from .user_sessions_impersonator import (
UserSessionsImpersonator as UserSessionsImpersonator,
Expand Down
Loading