Skip to content
Open
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,40 @@
* [#665](https://github.com/workos/workos-python/pull/665) feat(generated)!: regenerate from spec (9 changes)

**⚠️ Breaking**
* **user_management:** Remove return_to parameter from revoke_session
* Removed `return_to` parameter from `UserManagement.revoke_session` and `AsyncUserManagement.revoke_session` methods
* Updated `RevokeSession` model to remove the `return_to` field
* The parameter was optional but is now completely removed from the API
* **user_management:** Add name field to User, CreateUser, and UpdateUser models
* Added `name` field to `User` model for the user's full name
* Added `name` parameter to `create_user` and `update_user` methods
* Added `name` field to `CreateUser` and `UpdateUser` models
* Allows setting user's full name in addition to first and last name separately

**Features**
* **api_keys:** Add create_api_key_expire operation
* Added new `create_api_key_expire` method to both `ApiKeys` and `AsyncApiKeys` classes
* Added `ExpireApiKey` model with optional `expires_at` field
* New operation allows expiring an API key immediately, scheduling future expiration, or clearing scheduled expiration
* **common:** Remove DsyncDeactivated models and add DsyncToken events
* Removed `DsyncDeactivated`, `DsyncDeactivatedData`, `DsyncDeactivatedDataDomain`, `DsyncDeactivatedDataState`, and `DsyncDeactivatedDataType` models (breaking change)
* Added new event models: `DsyncTokenCreated`, `DsyncTokenCreatedData`, `DsyncTokenRevoked`, `DsyncTokenRevokedData`
* Updated event schema to reflect removal of `dsync.deactivated` event and addition of `dsync.token.created` and `dsync.token.revoked` events
* Updated webhook endpoint events to support new `api_key.updated` event
* **common:** Add ApiKeyUpdated event models
* Added `ApiKeyUpdated`, `ApiKeyUpdatedData`, `ApiKeyUpdatedDataOwner`, `ApiKeyUpdatedDataPreviousAttribute`, and `UserApiKeyUpdatedDataOwner` models
* New event models support tracking API key updates with previous attribute values
* Updated create and update webhook endpoint events to include `api_key.updated`
* **common:** Make expires_at required in ApiKeyCreatedData and ApiKeyRevokedData
* Changed `expires_at` field from optional to required in `ApiKeyCreatedData` model
* Changed `expires_at` field from optional to required in `ApiKeyRevokedData` model
* Reordered fields to move `expires_at` before `permissions` in serialization
* **connect:** Add name field to UserObject model
* Added `name` field to `UserObject` model for the user's full name
* Allows Connect endpoints to return and accept user's full name alongside first and last name
* **authorization:** Remove DOMAIN_SIGN_UP_RATE_LIMIT from RadarStandaloneResponseControl enum
* Removed `DOMAIN_SIGN_UP_RATE_LIMIT` value from `RadarStandaloneResponseControl` enum (breaking change)
* Updated enum type alias to exclude the removed value
* **audit_logs:** Add SNOWFLAKE to AuditLogConfigurationLogStreamType enum
* Added `SNOWFLAKE` value to `AuditLogConfigurationLogStreamType` enum
* Extends audit log configuration to support Snowflake as a log stream destination
2 changes: 1 addition & 1 deletion .last-synced-sha
Original file line number Diff line number Diff line change
@@ -1 +1 @@
cb6857d51b453e3cbdb5bf3647d3ca229dd8af65
d8c5a7de598792b1cee18d4a9842825110e5c74a
34 changes: 25 additions & 9 deletions .oagen-manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"version": 2,
"language": "python",
"generatedAt": "2026-05-26T19:47:29.645Z",
"generatedAt": "2026-06-03T19:19:55.137Z",
"files": [
"src/workos/_client.py",
"src/workos/admin_portal/__init__.py",
Expand All @@ -19,6 +19,7 @@
"src/workos/api_keys/models/api_key_owner.py",
"src/workos/api_keys/models/api_key_validation_response.py",
"src/workos/api_keys/models/create_organization_api_key.py",
"src/workos/api_keys/models/expire_api_key.py",
"src/workos/api_keys/models/organization_api_key.py",
"src/workos/api_keys/models/organization_api_key_owner.py",
"src/workos/api_keys/models/organization_api_key_with_value.py",
Expand Down Expand Up @@ -79,6 +80,10 @@
"src/workos/common/models/api_key_revoked.py",
"src/workos/common/models/api_key_revoked_data.py",
"src/workos/common/models/api_key_revoked_data_owner.py",
"src/workos/common/models/api_key_updated.py",
"src/workos/common/models/api_key_updated_data.py",
"src/workos/common/models/api_key_updated_data_owner.py",
"src/workos/common/models/api_key_updated_data_previous_attribute.py",
"src/workos/common/models/audit_log_configuration_log_stream_state.py",
"src/workos/common/models/audit_log_configuration_log_stream_type.py",
"src/workos/common/models/audit_log_configuration_state.py",
Expand Down Expand Up @@ -195,11 +200,6 @@
"src/workos/common/models/dsync_activated_data_domain.py",
"src/workos/common/models/dsync_activated_data_state.py",
"src/workos/common/models/dsync_activated_data_type.py",
"src/workos/common/models/dsync_deactivated.py",
"src/workos/common/models/dsync_deactivated_data.py",
"src/workos/common/models/dsync_deactivated_data_domain.py",
"src/workos/common/models/dsync_deactivated_data_state.py",
"src/workos/common/models/dsync_deactivated_data_type.py",
"src/workos/common/models/dsync_deleted.py",
"src/workos/common/models/dsync_deleted_data.py",
"src/workos/common/models/dsync_deleted_data_state.py",
Expand All @@ -212,6 +212,10 @@
"src/workos/common/models/dsync_group_user_added_data.py",
"src/workos/common/models/dsync_group_user_removed.py",
"src/workos/common/models/dsync_group_user_removed_data.py",
"src/workos/common/models/dsync_token_created.py",
"src/workos/common/models/dsync_token_created_data.py",
"src/workos/common/models/dsync_token_revoked.py",
"src/workos/common/models/dsync_token_revoked_data.py",
"src/workos/common/models/dsync_user_created.py",
"src/workos/common/models/dsync_user_deleted.py",
"src/workos/common/models/dsync_user_updated.py",
Expand Down Expand Up @@ -396,6 +400,7 @@
"src/workos/common/models/user.py",
"src/workos/common/models/user_api_key_created_data_owner.py",
"src/workos/common/models/user_api_key_revoked_data_owner.py",
"src/workos/common/models/user_api_key_updated_data_owner.py",
"src/workos/common/models/user_created.py",
"src/workos/common/models/user_deleted.py",
"src/workos/common/models/user_identities_get_item_provider.py",
Expand Down Expand Up @@ -674,6 +679,10 @@
"tests/fixtures/api_key_revoked.json",
"tests/fixtures/api_key_revoked_data.json",
"tests/fixtures/api_key_revoked_data_owner.json",
"tests/fixtures/api_key_updated.json",
"tests/fixtures/api_key_updated_data.json",
"tests/fixtures/api_key_updated_data_owner.json",
"tests/fixtures/api_key_updated_data_previous_attribute.json",
"tests/fixtures/api_key_validation_response.json",
"tests/fixtures/application_credentials_list_item.json",
"tests/fixtures/assign_role.json",
Expand Down Expand Up @@ -835,9 +844,6 @@
"tests/fixtures/dsync_activated.json",
"tests/fixtures/dsync_activated_data.json",
"tests/fixtures/dsync_activated_data_domain.json",
"tests/fixtures/dsync_deactivated.json",
"tests/fixtures/dsync_deactivated_data.json",
"tests/fixtures/dsync_deactivated_data_domain.json",
"tests/fixtures/dsync_deleted.json",
"tests/fixtures/dsync_deleted_data.json",
"tests/fixtures/dsync_group_created.json",
Expand All @@ -848,6 +854,10 @@
"tests/fixtures/dsync_group_user_added_data.json",
"tests/fixtures/dsync_group_user_removed.json",
"tests/fixtures/dsync_group_user_removed_data.json",
"tests/fixtures/dsync_token_created.json",
"tests/fixtures/dsync_token_created_data.json",
"tests/fixtures/dsync_token_revoked.json",
"tests/fixtures/dsync_token_revoked_data.json",
"tests/fixtures/dsync_user_created.json",
"tests/fixtures/dsync_user_deleted.json",
"tests/fixtures/dsync_user_updated.json",
Expand All @@ -867,6 +877,7 @@
"tests/fixtures/event_context_google_analytics_session.json",
"tests/fixtures/event_list_list_metadata.json",
"tests/fixtures/event_schema.json",
"tests/fixtures/expire_api_key.json",
"tests/fixtures/external_auth_complete_response.json",
"tests/fixtures/feature_flag.json",
"tests/fixtures/feature_flag_owner.json",
Expand Down Expand Up @@ -1081,6 +1092,7 @@
"tests/fixtures/user_api_key_created_data_owner.json",
"tests/fixtures/user_api_key_owner.json",
"tests/fixtures/user_api_key_revoked_data_owner.json",
"tests/fixtures/user_api_key_updated_data_owner.json",
"tests/fixtures/user_api_key_with_value.json",
"tests/fixtures/user_api_key_with_value_owner.json",
"tests/fixtures/user_authentication_factor_enroll_response.json",
Expand Down Expand Up @@ -1856,6 +1868,10 @@
"sdkMethod": "delete_api_key",
"service": "api_keys"
},
"POST /api_keys/{id}/expire": {
"sdkMethod": "create_api_key_expire",
"service": "api_keys"
},
"GET /user_management/users/{userId}/api_keys": {
"sdkMethod": "list_user_api_keys",
"service": "user_management"
Expand Down
85 changes: 85 additions & 0 deletions src/workos/api_keys/_resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

from .._types import RequestOptions, enum_value
from .models import (
ApiKey,
ApiKeyValidationResponse,
OrganizationApiKey,
OrganizationApiKeyWithValue,
Expand Down Expand Up @@ -179,6 +180,48 @@ def delete_api_key(
request_options=request_options,
)

def create_api_key_expire(
self,
id: str,
*,
expires_at: Optional[str] = None,
request_options: Optional[RequestOptions] = None,
) -> ApiKey:
"""Expire an API key

Expire an API key immediately, schedule a future expiration, or clear a scheduled future expiration.

Args:
id: The unique ID of the API key.
expires_at: When the API key should expire. If omitted or in the past, the key expires immediately. Use null to clear a scheduled future expiration.
request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override.

Returns:
ApiKey

Raises:
NotFoundError: If the resource is not found (404).
ConflictError: If a conflict occurs (409).
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 {
"expires_at": expires_at,
}.items()
if v is not None
}
Comment on lines +210 to +216

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 Cannot send explicit null to clear scheduled expiration

The docstring for expires_at states: "Use null to clear a scheduled future expiration." However the body-building filter if v is not None silently drops a None value, so calling create_api_key_expire(id, expires_at=None) produces an empty body {} — identical to omitting the argument. There is no way for callers to send {"expires_at": null} to the API, making the "clear scheduled expiration" use case completely unreachable. The same issue is present in AsyncApiKeys.create_api_key_expire at line 415.

return self._client.request(
method="post",
path=("api_keys", str(id), "expire"),
body=body,
model=ApiKey,
request_options=request_options,
)


class AsyncApiKeys:
"""Api Keys API resources (async)."""
Expand Down Expand Up @@ -341,3 +384,45 @@ async def delete_api_key(
path=("api_keys", str(id)),
request_options=request_options,
)

async def create_api_key_expire(
self,
id: str,
*,
expires_at: Optional[str] = None,
request_options: Optional[RequestOptions] = None,
) -> ApiKey:
"""Expire an API key

Expire an API key immediately, schedule a future expiration, or clear a scheduled future expiration.

Args:
id: The unique ID of the API key.
expires_at: When the API key should expire. If omitted or in the past, the key expires immediately. Use null to clear a scheduled future expiration.
request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override.

Returns:
ApiKey

Raises:
NotFoundError: If the resource is not found (404).
ConflictError: If a conflict occurs (409).
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 {
"expires_at": expires_at,
}.items()
if v is not None
}
return await self._client.request(
method="post",
path=("api_keys", str(id), "expire"),
body=body,
model=ApiKey,
request_options=request_options,
)
1 change: 1 addition & 0 deletions src/workos/api_keys/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from .create_organization_api_key import (
CreateOrganizationApiKey as CreateOrganizationApiKey,
)
from .expire_api_key import ExpireApiKey as ExpireApiKey
from .organization_api_key import OrganizationApiKey as OrganizationApiKey
from .organization_api_key_owner import (
OrganizationApiKeyOwner as OrganizationApiKeyOwner,
Expand Down
38 changes: 38 additions & 0 deletions src/workos/api_keys/models/expire_api_key.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# 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, Dict, Optional
from workos._types import _raise_deserialize_error
from workos._types import _format_datetime, _parse_datetime


@dataclass(slots=True)
class ExpireApiKey:
"""Expire Api Key model."""

expires_at: Optional[datetime] = None
"""When the API key should expire. If omitted or in the past, the key expires immediately. Use null to clear a scheduled future expiration."""

@classmethod
def from_dict(cls, data: Dict[str, Any]) -> "ExpireApiKey":
"""Deserialize from a dictionary."""
try:
return cls(
expires_at=_parse_datetime(_v_expires_at)
if (_v_expires_at := data.get("expires_at")) is not None
else None,
)
except (KeyError, ValueError) as e:
_raise_deserialize_error("ExpireApiKey", e)

def to_dict(self) -> Dict[str, Any]:
"""Serialize to a dictionary."""
result: Dict[str, Any] = {}
if self.expires_at is not None:
result["expires_at"] = _format_datetime(self.expires_at)
else:
result["expires_at"] = None
return result
Loading