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
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "0.16.0"
".": "0.17.0"
}
4 changes: 2 additions & 2 deletions .stats.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
configured_endpoints: 106
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/keycard/keycard-api-cdf14bee924cea2eaca3c1aaed26521cb91369d9462d22c21a0306b5786f4706.yml
openapi_spec_hash: d8349acc1adec880977b6167a1e866bc
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/keycard/keycard-api-9a353824f6b68547b91d52205e052e4a5428ae35f37a10fb898aa8a38458efeb.yml
openapi_spec_hash: 846e320252df771be0cb6c47e876bcf1
config_hash: 7adc6b24545570dcc4a1bf0f714aa3e0
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
# Changelog

## 0.17.0 (2026-07-06)

Full Changelog: [v0.16.0...v0.17.0](https://github.com/keycardai/keycard-python/compare/v0.16.0...v0.17.0)

### Features

* filter users by identifier in management list ([147af02](https://github.com/keycardai/keycard-python/commit/147af020e8e8c31678928137fb85386b54dd4ec6))
* **ID-365:** support additional SSO provider configuration options ([526b229](https://github.com/keycardai/keycard-python/commit/526b22958acc91a11569ba3f8453a004eb03f6f5))


### Bug Fixes

* **ACC-613:** preserve source order of policies in draft/convert cedar_json ([6d27d21](https://github.com/keycardai/keycard-python/commit/6d27d213ac66fb9f1890409a380924c3edb44b99))
* exact-match identifier filter on resources management list ([846f24e](https://github.com/keycardai/keycard-python/commit/846f24efdb2492e4ea0741167ebc7153af5d0d1e))

## 0.16.0 (2026-06-30)

Full Changelog: [v0.15.0...v0.16.0](https://github.com/keycardai/keycard-python/compare/v0.15.0...v0.16.0)
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "keycardai_api"
version = "0.16.0"
version = "0.17.0"
description = "The official Python library for the keycard-api API"
dynamic = ["readme"]
license = "Apache-2.0"
Expand Down
2 changes: 1 addition & 1 deletion src/keycardai_api/_version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

__title__ = "keycardai_api"
__version__ = "0.16.0" # x-release-please-version
__version__ = "0.17.0" # x-release-please-version
10 changes: 6 additions & 4 deletions src/keycardai_api/resources/organizations/sso_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ def update(
client_id: str | Omit = omit,
client_secret: str | Omit = omit,
identifier: str | Omit = omit,
protocols: Optional[SSOConnectionProtocolParam] | Omit = omit,
protocols: Optional[sso_connection_update_params.Protocols] | Omit = omit,
x_client_request_id: str | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
Expand All @@ -125,7 +125,8 @@ def update(

identifier: SSO provider identifier (e.g., issuer URL)

protocols: Protocol configuration for SSO connection
protocols: Protocol configuration for an SSO connection update. Omit a protocol to leave it
unchanged.

extra_headers: Send extra headers

Expand Down Expand Up @@ -329,7 +330,7 @@ async def update(
client_id: str | Omit = omit,
client_secret: str | Omit = omit,
identifier: str | Omit = omit,
protocols: Optional[SSOConnectionProtocolParam] | Omit = omit,
protocols: Optional[sso_connection_update_params.Protocols] | Omit = omit,
x_client_request_id: str | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
Expand All @@ -350,7 +351,8 @@ async def update(

identifier: SSO provider identifier (e.g., issuer URL)

protocols: Protocol configuration for SSO connection
protocols: Protocol configuration for an SSO connection update. Omit a protocol to leave it
unchanged.

extra_headers: Send extra headers

Expand Down
24 changes: 20 additions & 4 deletions src/keycardai_api/resources/zones/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ def list(
before: str | Omit = omit,
credential_provider_id: str | Omit = omit,
expand: Union[Literal["total_count"], List[Literal["total_count"]]] | Omit = omit,
filter_identifier: Union[str, SequenceNotStr[str]] | Omit = omit,
identifier: str | Omit = omit,
limit: int | Omit = omit,
slug: str | Omit = omit,
Expand All @@ -281,8 +282,12 @@ def list(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> ResourceListResponse:
"""
Returns a list of resources in the specified zone
"""Returns a list of resources in the specified zone.

Filter by exact identifier
via `filter[identifier]` (repeatable, OR'd). Matching is exact: identifiers are
unique per zone, so a filter returns at most one resource per value and never
performs URL prefix resolution.

Args:
after: Cursor for forward pagination
Expand All @@ -291,6 +296,8 @@ def list(

credential_provider_id: Filter resources by credential provider ID

filter_identifier: Filter by exact resource identifier

identifier: Filter resources by identifier

limit: Maximum number of items to return
Expand Down Expand Up @@ -318,6 +325,7 @@ def list(
"before": before,
"credential_provider_id": credential_provider_id,
"expand": expand,
"filter_identifier": filter_identifier,
"identifier": identifier,
"limit": limit,
"slug": slug,
Expand Down Expand Up @@ -606,6 +614,7 @@ async def list(
before: str | Omit = omit,
credential_provider_id: str | Omit = omit,
expand: Union[Literal["total_count"], List[Literal["total_count"]]] | Omit = omit,
filter_identifier: Union[str, SequenceNotStr[str]] | Omit = omit,
identifier: str | Omit = omit,
limit: int | Omit = omit,
slug: str | Omit = omit,
Expand All @@ -616,8 +625,12 @@ async def list(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> ResourceListResponse:
"""
Returns a list of resources in the specified zone
"""Returns a list of resources in the specified zone.

Filter by exact identifier
via `filter[identifier]` (repeatable, OR'd). Matching is exact: identifiers are
unique per zone, so a filter returns at most one resource per value and never
performs URL prefix resolution.

Args:
after: Cursor for forward pagination
Expand All @@ -626,6 +639,8 @@ async def list(

credential_provider_id: Filter resources by credential provider ID

filter_identifier: Filter by exact resource identifier

identifier: Filter resources by identifier

limit: Maximum number of items to return
Expand Down Expand Up @@ -653,6 +668,7 @@ async def list(
"before": before,
"credential_provider_id": credential_provider_id,
"expand": expand,
"filter_identifier": filter_identifier,
"identifier": identifier,
"limit": limit,
"slug": slug,
Expand Down
66 changes: 36 additions & 30 deletions src/keycardai_api/resources/zones/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ def list(
| Omit = omit,
filter_email: Union[str, SequenceNotStr[str]] | Omit = omit,
filter_id: Union[str, SequenceNotStr[str]] | Omit = omit,
filter_identifier: Union[str, SequenceNotStr[str]] | Omit = omit,
limit: int | Omit = omit,
query: Union[str, SequenceNotStr[str]] | Omit = omit,
query_email: Union[str, SequenceNotStr[str]] | Omit = omit,
Expand All @@ -109,27 +110,26 @@ def list(
"""
Returns a list of users in the specified zone.

**Rollout note:** the paginated/searchable/sortable behavior described below is
gated behind the `user-pagination` feature flag and is currently disabled for
most zones. While the flag is off, the response returns every user in the zone
(capped at 100) in `items` and a fixed pagination envelope where `after_cursor`
and `before_cursor` are `null` and `total_count` is `0`. The query parameters
below are accepted but ignored. The flag is rolled out per-zone in Datadog and
will become the default once Console adopts the paginated contract.
Note: cursor pagination, search, and sort are not yet enabled for all zones.
Where they are not enabled, the response returns all users in the zone (capped
at 100) in `items`, with `after_cursor` and `before_cursor` set to `null` and
`total_count` of `0`; `filter[email]` and `filter[identifier]` are still
applied, while the pagination, search, and sort parameters below are accepted
but ignored.

Use cursor pagination via `after`/`before`. Sort: comma-separated field list;
prefix with `-` for descending. Use `expand[]=total_count` to include the
matching row count, `expand[]=session_count` to include per-user session counts,
`expand[]=grant_count` to include per-user delegated-grant counts, and
`expand[]=role-assignments` to include each user's structured role grants.
Filter by exact email via `filter[email]`; search via `query[email]` /
`query[subject]` / `query[]` (substring match, OR'd across repeated values).
`query[]` matches against email and federation credential subject. Pass
`filter[id]` (repeatable, max 100) to restrict results to a known set of users —
mutually exclusive with `after`/`before` (returns 400 if combined). When
`filter[id]` is set, `limit` is ignored and the response contains every
requested user that exists in the zone, in a single page. IDs not in the zone
are silently omitted.
Filter by exact email via `filter[email]` and by exact identifier via
`filter[identifier]`; search via `query[email]` / `query[subject]` / `query[]`
(substring match, OR'd across repeated values). `query[]` matches against email
and federation credential subject. Pass `filter[id]` (repeatable, max 100) to
restrict results to a known set of users — mutually exclusive with
`after`/`before` (returns 400 if combined). When `filter[id]` is set, `limit` is
ignored and the response contains every requested user that exists in the zone,
in a single page. IDs not in the zone are silently omitted.

Args:
after: Cursor for forward pagination
Expand All @@ -141,6 +141,8 @@ def list(
filter_id: Restrict results to users with this publicId. Repeatable, max 100. Mutually
exclusive with after/before.

filter_identifier: Filter by exact user identifier

limit: Maximum number of items to return

query: Search across email and credential subject (substring match)
Expand Down Expand Up @@ -176,6 +178,7 @@ def list(
"expand": expand,
"filter_email": filter_email,
"filter_id": filter_id,
"filter_identifier": filter_identifier,
"limit": limit,
"query": query,
"query_email": query_email,
Expand Down Expand Up @@ -258,6 +261,7 @@ async def list(
| Omit = omit,
filter_email: Union[str, SequenceNotStr[str]] | Omit = omit,
filter_id: Union[str, SequenceNotStr[str]] | Omit = omit,
filter_identifier: Union[str, SequenceNotStr[str]] | Omit = omit,
limit: int | Omit = omit,
query: Union[str, SequenceNotStr[str]] | Omit = omit,
query_email: Union[str, SequenceNotStr[str]] | Omit = omit,
Expand All @@ -273,27 +277,26 @@ async def list(
"""
Returns a list of users in the specified zone.

**Rollout note:** the paginated/searchable/sortable behavior described below is
gated behind the `user-pagination` feature flag and is currently disabled for
most zones. While the flag is off, the response returns every user in the zone
(capped at 100) in `items` and a fixed pagination envelope where `after_cursor`
and `before_cursor` are `null` and `total_count` is `0`. The query parameters
below are accepted but ignored. The flag is rolled out per-zone in Datadog and
will become the default once Console adopts the paginated contract.
Note: cursor pagination, search, and sort are not yet enabled for all zones.
Where they are not enabled, the response returns all users in the zone (capped
at 100) in `items`, with `after_cursor` and `before_cursor` set to `null` and
`total_count` of `0`; `filter[email]` and `filter[identifier]` are still
applied, while the pagination, search, and sort parameters below are accepted
but ignored.

Use cursor pagination via `after`/`before`. Sort: comma-separated field list;
prefix with `-` for descending. Use `expand[]=total_count` to include the
matching row count, `expand[]=session_count` to include per-user session counts,
`expand[]=grant_count` to include per-user delegated-grant counts, and
`expand[]=role-assignments` to include each user's structured role grants.
Filter by exact email via `filter[email]`; search via `query[email]` /
`query[subject]` / `query[]` (substring match, OR'd across repeated values).
`query[]` matches against email and federation credential subject. Pass
`filter[id]` (repeatable, max 100) to restrict results to a known set of users —
mutually exclusive with `after`/`before` (returns 400 if combined). When
`filter[id]` is set, `limit` is ignored and the response contains every
requested user that exists in the zone, in a single page. IDs not in the zone
are silently omitted.
Filter by exact email via `filter[email]` and by exact identifier via
`filter[identifier]`; search via `query[email]` / `query[subject]` / `query[]`
(substring match, OR'd across repeated values). `query[]` matches against email
and federation credential subject. Pass `filter[id]` (repeatable, max 100) to
restrict results to a known set of users — mutually exclusive with
`after`/`before` (returns 400 if combined). When `filter[id]` is set, `limit` is
ignored and the response contains every requested user that exists in the zone,
in a single page. IDs not in the zone are silently omitted.

Args:
after: Cursor for forward pagination
Expand All @@ -305,6 +308,8 @@ async def list(
filter_id: Restrict results to users with this publicId. Repeatable, max 100. Mutually
exclusive with after/before.

filter_identifier: Filter by exact user identifier

limit: Maximum number of items to return

query: Search across email and credential subject (substring match)
Expand Down Expand Up @@ -340,6 +345,7 @@ async def list(
"expand": expand,
"filter_email": filter_email,
"filter_id": filter_id,
"filter_identifier": filter_identifier,
"limit": limit,
"query": query,
"query_email": query_email,
Expand Down
21 changes: 20 additions & 1 deletion src/keycardai_api/types/organizations/sso_connection_protocol.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

from typing import List, Optional
from typing import Dict, List, Optional

from ..._models import BaseModel

Expand All @@ -13,6 +13,13 @@ class Oauth2(BaseModel):
authorization_endpoint: Optional[str] = None
"""OAuth 2.0 authorization endpoint"""

authorization_parameters: Optional[Dict[str, str]] = None
"""Custom query parameters appended to authorization redirect URLs.

Use for non-standard providers (e.g. Google prompt=consent,
access_type=offline).
"""

code_challenge_methods_supported: Optional[List[str]] = None
"""Supported PKCE code challenge methods"""

Expand All @@ -32,6 +39,18 @@ class Oauth2(BaseModel):
class Openid(BaseModel):
"""OpenID Connect protocol configuration for SSO connection"""

scopes: Optional[List[str]] = None
"""Additional OIDC scopes to request from this provider during authentication (e.g.

"groups"). Merged with the default scopes (openid, profile, email).
"""

user_identifier_claim: Optional[str] = None
"""
Name of a top-level string claim in the provider's ID Token to use as the user
identifier on user creation. When not set, the user's Keycard ID is used.
"""

userinfo_endpoint: Optional[str] = None
"""OpenID Connect UserInfo endpoint"""

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from __future__ import annotations

from typing import Optional
from typing import Dict, Optional
from typing_extensions import TypedDict

from ..._types import SequenceNotStr
Expand All @@ -16,6 +16,13 @@ class Oauth2(TypedDict, total=False):
authorization_endpoint: Optional[str]
"""OAuth 2.0 authorization endpoint"""

authorization_parameters: Optional[Dict[str, str]]
"""Custom query parameters appended to authorization redirect URLs.

Use for non-standard providers (e.g. Google prompt=consent,
access_type=offline).
"""

code_challenge_methods_supported: Optional[SequenceNotStr[str]]
"""Supported PKCE code challenge methods"""

Expand All @@ -35,6 +42,18 @@ class Oauth2(TypedDict, total=False):
class Openid(TypedDict, total=False):
"""OpenID Connect protocol configuration for SSO connection"""

scopes: Optional[SequenceNotStr[str]]
"""Additional OIDC scopes to request from this provider during authentication (e.g.

"groups"). Merged with the default scopes (openid, profile, email).
"""

user_identifier_claim: Optional[str]
"""
Name of a top-level string claim in the provider's ID Token to use as the user
identifier on user creation. When not set, the user's Keycard ID is used.
"""

userinfo_endpoint: Optional[str]
"""OpenID Connect UserInfo endpoint"""

Expand Down
Loading
Loading