diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 7518d9c..92182d3 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -19,7 +19,7 @@ jobs:
timeout-minutes: 10
name: lint
runs-on: ${{ github.repository == 'stainless-sdks/keycard-api-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
- if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
+ if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata')
steps:
- uses: actions/checkout@v6
@@ -35,7 +35,7 @@ jobs:
run: ./scripts/lint
build:
- if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
+ if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata')
timeout-minutes: 10
name: build
permissions:
diff --git a/.gitignore b/.gitignore
index 95ceb18..3824f4c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
.prism.log
+.stdy.log
_dev
__pycache__
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 6b7b74c..da59f99 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.3.0"
+ ".": "0.4.0"
}
\ No newline at end of file
diff --git a/.stats.yml b/.stats.yml
index c2d3476..f7292bc 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 107
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/keycard%2Fkeycard-api-e0b7b296f7b0cac79675a790974f3ad90eb46833027467ee97c3ed21675628b8.yml
-openapi_spec_hash: 5e60faefb18dd2fca721f14252ab907a
-config_hash: dbb3c94874836d520e64fc8ffda4f0d3
+configured_endpoints: 106
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/keycard%2Fkeycard-api-f935374f57248d24e5f8a2393ce0730503bc466f7c51e93b6d3225d5b9317baf.yml
+openapi_spec_hash: de0d11f4dcc9810bcb0d6bb8882e21a4
+config_hash: fab2044922865fa04fb03f5f19e195a0
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 70e6ee0..4c25f3c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,30 @@
# Changelog
+## 0.4.0 (2026-04-03)
+
+Full Changelog: [v0.3.0...v0.4.0](https://github.com/keycardai/keycard-python/compare/v0.3.0...v0.4.0)
+
+### Features
+
+* add email search to list organization identities endpoint ([f9b2707](https://github.com/keycardai/keycard-python/commit/f9b2707b0649678407423d2f5b3007a6d7bcc2e6))
+* add owner_type and enforce protection for platform-owned versions (ACC-29) ([bfac76d](https://github.com/keycardai/keycard-python/commit/bfac76da47db6264d00d9df73c387a8e5e175840))
+* add PRM discovery to MCP gateway endpoint ([fbc502d](https://github.com/keycardai/keycard-python/commit/fbc502dc075d7a0f6890317fc59203ca51d644a7))
+* improved identities pagination ([d4c4d1b](https://github.com/keycardai/keycard-python/commit/d4c4d1ba4d76d709e6067d36f1848d293d227a81))
+* **internal:** implement indices array format for query and form serialization ([617099d](https://github.com/keycardai/keycard-python/commit/617099df6ed00538530a5f4ed285130dd5ac13c7))
+* provide more context for policy schema ([46bea8a](https://github.com/keycardai/keycard-python/commit/46bea8a4493318b0b37904119e057cb7b4e50550))
+* Support for user identifier and provider user identifier claim ([ac47cc2](https://github.com/keycardai/keycard-python/commit/ac47cc20c48168e226a78d5402d7f8e745881bb8))
+
+
+### Bug Fixes
+
+* sanitize endpoint path params ([6993e56](https://github.com/keycardai/keycard-python/commit/6993e56d5e9a79713471a0b2b33920c1e2da77d9))
+
+
+### Chores
+
+* **ci:** skip lint on metadata-only changes ([bc52e3f](https://github.com/keycardai/keycard-python/commit/bc52e3f59616d0754b7f0bc30374fac7f357f63b))
+* **internal:** update gitignore ([24f9a15](https://github.com/keycardai/keycard-python/commit/24f9a15a6031769c2a5912a3eda480fb82fc17b1))
+
## 0.3.0 (2026-03-16)
Full Changelog: [v0.2.0...v0.3.0](https://github.com/keycardai/keycard-python/compare/v0.2.0...v0.3.0)
diff --git a/README.md b/README.md
index fabc50f..cf222f1 100644
--- a/README.md
+++ b/README.md
@@ -9,15 +9,6 @@ and offers both synchronous and asynchronous clients powered by [httpx](https://
It is generated with [Stainless](https://www.stainless.com/).
-## MCP Server
-
-Use the Keycard API MCP Server to enable AI assistants to interact with this API, allowing them to explore endpoints, make test requests, and use documentation to help integrate this SDK into your application.
-
-[](https://cursor.com/en-US/install-mcp?name=%40keycardai%2Fapi-mcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBrZXljYXJkYWkvYXBpLW1jcCJdLCJlbnYiOnsiS0VZQ0FSRF9BUElfQVBJX0tFWSI6Ik15IEFQSSBLZXkiLCJLRVlDQVJEX0FQSV9DTElFTlRfSUQiOiJNeSBDbGllbnQgSUQiLCJLRVlDQVJEX0FQSV9DTElFTlRfU0VDUkVUIjoiTXkgQ2xpZW50IFNlY3JldCJ9fQ)
-[](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22%40keycardai%2Fapi-mcp%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40keycardai%2Fapi-mcp%22%5D%2C%22env%22%3A%7B%22KEYCARD_API_API_KEY%22%3A%22My%20API%20Key%22%2C%22KEYCARD_API_CLIENT_ID%22%3A%22My%20Client%20ID%22%2C%22KEYCARD_API_CLIENT_SECRET%22%3A%22My%20Client%20Secret%22%7D%7D)
-
-> Note: You may need to set environment variables in your MCP client.
-
## Documentation
The REST API documentation can be found on [docs.keycard.ai](https://docs.keycard.ai). The full API of this library can be found in [api.md](api.md).
diff --git a/api.md b/api.md
index 2fe229e..de50220 100644
--- a/api.md
+++ b/api.md
@@ -8,7 +8,6 @@ from keycardai_api.types import (
PageInfoPagination,
Zone,
ZoneListResponse,
- ZoneListSessionResourceAccessResponse,
)
```
@@ -19,7 +18,6 @@ Methods:
- client.zones.update(zone_id, \*\*params) -> Zone
- client.zones.list(\*\*params) -> ZoneListResponse
- client.zones.delete(zone_id) -> None
-- client.zones.list_session_resource_access(zone_id, \*\*params) -> ZoneListSessionResourceAccessResponse
## Applications
diff --git a/pyproject.toml b/pyproject.toml
index 77ffed2..5b59e67 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "keycardai_api"
-version = "0.3.0"
+version = "0.4.0"
description = "The official Python library for the keycard-api API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/keycardai_api/_qs.py b/src/keycardai_api/_qs.py
index ada6fd3..de8c99b 100644
--- a/src/keycardai_api/_qs.py
+++ b/src/keycardai_api/_qs.py
@@ -101,7 +101,10 @@ def _stringify_item(
items.extend(self._stringify_item(key, item, opts))
return items
elif array_format == "indices":
- raise NotImplementedError("The array indices format is not supported yet")
+ items = []
+ for i, item in enumerate(value):
+ items.extend(self._stringify_item(f"{key}[{i}]", item, opts))
+ return items
elif array_format == "brackets":
items = []
key = key + "[]"
diff --git a/src/keycardai_api/_utils/__init__.py b/src/keycardai_api/_utils/__init__.py
index dc64e29..10cb66d 100644
--- a/src/keycardai_api/_utils/__init__.py
+++ b/src/keycardai_api/_utils/__init__.py
@@ -1,3 +1,4 @@
+from ._path import path_template as path_template
from ._sync import asyncify as asyncify
from ._proxy import LazyProxy as LazyProxy
from ._utils import (
diff --git a/src/keycardai_api/_utils/_path.py b/src/keycardai_api/_utils/_path.py
new file mode 100644
index 0000000..4d6e1e4
--- /dev/null
+++ b/src/keycardai_api/_utils/_path.py
@@ -0,0 +1,127 @@
+from __future__ import annotations
+
+import re
+from typing import (
+ Any,
+ Mapping,
+ Callable,
+)
+from urllib.parse import quote
+
+# Matches '.' or '..' where each dot is either literal or percent-encoded (%2e / %2E).
+_DOT_SEGMENT_RE = re.compile(r"^(?:\.|%2[eE]){1,2}$")
+
+_PLACEHOLDER_RE = re.compile(r"\{(\w+)\}")
+
+
+def _quote_path_segment_part(value: str) -> str:
+ """Percent-encode `value` for use in a URI path segment.
+
+ Considers characters not in `pchar` set from RFC 3986 §3.3 to be unsafe.
+ https://datatracker.ietf.org/doc/html/rfc3986#section-3.3
+ """
+ # quote() already treats unreserved characters (letters, digits, and -._~)
+ # as safe, so we only need to add sub-delims, ':', and '@'.
+ # Notably, unlike the default `safe` for quote(), / is unsafe and must be quoted.
+ return quote(value, safe="!$&'()*+,;=:@")
+
+
+def _quote_query_part(value: str) -> str:
+ """Percent-encode `value` for use in a URI query string.
+
+ Considers &, = and characters not in `query` set from RFC 3986 §3.4 to be unsafe.
+ https://datatracker.ietf.org/doc/html/rfc3986#section-3.4
+ """
+ return quote(value, safe="!$'()*+,;:@/?")
+
+
+def _quote_fragment_part(value: str) -> str:
+ """Percent-encode `value` for use in a URI fragment.
+
+ Considers characters not in `fragment` set from RFC 3986 §3.5 to be unsafe.
+ https://datatracker.ietf.org/doc/html/rfc3986#section-3.5
+ """
+ return quote(value, safe="!$&'()*+,;=:@/?")
+
+
+def _interpolate(
+ template: str,
+ values: Mapping[str, Any],
+ quoter: Callable[[str], str],
+) -> str:
+ """Replace {name} placeholders in `template`, quoting each value with `quoter`.
+
+ Placeholder names are looked up in `values`.
+
+ Raises:
+ KeyError: If a placeholder is not found in `values`.
+ """
+ # re.split with a capturing group returns alternating
+ # [text, name, text, name, ..., text] elements.
+ parts = _PLACEHOLDER_RE.split(template)
+
+ for i in range(1, len(parts), 2):
+ name = parts[i]
+ if name not in values:
+ raise KeyError(f"a value for placeholder {{{name}}} was not provided")
+ val = values[name]
+ if val is None:
+ parts[i] = "null"
+ elif isinstance(val, bool):
+ parts[i] = "true" if val else "false"
+ else:
+ parts[i] = quoter(str(values[name]))
+
+ return "".join(parts)
+
+
+def path_template(template: str, /, **kwargs: Any) -> str:
+ """Interpolate {name} placeholders in `template` from keyword arguments.
+
+ Args:
+ template: The template string containing {name} placeholders.
+ **kwargs: Keyword arguments to interpolate into the template.
+
+ Returns:
+ The template with placeholders interpolated and percent-encoded.
+
+ Safe characters for percent-encoding are dependent on the URI component.
+ Placeholders in path and fragment portions are percent-encoded where the `segment`
+ and `fragment` sets from RFC 3986 respectively are considered safe.
+ Placeholders in the query portion are percent-encoded where the `query` set from
+ RFC 3986 §3.3 is considered safe except for = and & characters.
+
+ Raises:
+ KeyError: If a placeholder is not found in `kwargs`.
+ ValueError: If resulting path contains /./ or /../ segments (including percent-encoded dot-segments).
+ """
+ # Split the template into path, query, and fragment portions.
+ fragment_template: str | None = None
+ query_template: str | None = None
+
+ rest = template
+ if "#" in rest:
+ rest, fragment_template = rest.split("#", 1)
+ if "?" in rest:
+ rest, query_template = rest.split("?", 1)
+ path_template = rest
+
+ # Interpolate each portion with the appropriate quoting rules.
+ path_result = _interpolate(path_template, kwargs, _quote_path_segment_part)
+
+ # Reject dot-segments (. and ..) in the final assembled path. The check
+ # runs after interpolation so that adjacent placeholders or a mix of static
+ # text and placeholders that together form a dot-segment are caught.
+ # Also reject percent-encoded dot-segments to protect against incorrectly
+ # implemented normalization in servers/proxies.
+ for segment in path_result.split("/"):
+ if _DOT_SEGMENT_RE.match(segment):
+ raise ValueError(f"Constructed path {path_result!r} contains dot-segment {segment!r} which is not allowed")
+
+ result = path_result
+ if query_template is not None:
+ result += "?" + _interpolate(query_template, kwargs, _quote_query_part)
+ if fragment_template is not None:
+ result += "#" + _interpolate(fragment_template, kwargs, _quote_fragment_part)
+
+ return result
diff --git a/src/keycardai_api/_version.py b/src/keycardai_api/_version.py
index 239bc4c..6bfff43 100644
--- a/src/keycardai_api/_version.py
+++ b/src/keycardai_api/_version.py
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
__title__ = "keycardai_api"
-__version__ = "0.3.0" # x-release-please-version
+__version__ = "0.4.0" # x-release-please-version
diff --git a/src/keycardai_api/resources/invitations.py b/src/keycardai_api/resources/invitations.py
index 57f8339..923bb58 100644
--- a/src/keycardai_api/resources/invitations.py
+++ b/src/keycardai_api/resources/invitations.py
@@ -5,7 +5,7 @@
import httpx
from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
-from .._utils import strip_not_given
+from .._utils import path_template, strip_not_given
from .._compat import cached_property
from .._resource import SyncAPIResource, AsyncAPIResource
from .._response import (
@@ -69,7 +69,7 @@ def retrieve(
raise ValueError(f"Expected a non-empty value for `token` but received {token!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return self._get(
- f"/invitations/{token}",
+ path_template("/invitations/{token}", token=token),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -108,7 +108,7 @@ def accept(
raise ValueError(f"Expected a non-empty value for `token` but received {token!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return self._post(
- f"/invitations/{token}/accept",
+ path_template("/invitations/{token}/accept", token=token),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -164,7 +164,7 @@ async def retrieve(
raise ValueError(f"Expected a non-empty value for `token` but received {token!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return await self._get(
- f"/invitations/{token}",
+ path_template("/invitations/{token}", token=token),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -203,7 +203,7 @@ async def accept(
raise ValueError(f"Expected a non-empty value for `token` but received {token!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return await self._post(
- f"/invitations/{token}/accept",
+ path_template("/invitations/{token}/accept", token=token),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
diff --git a/src/keycardai_api/resources/organizations/invitations.py b/src/keycardai_api/resources/organizations/invitations.py
index 8a71cc2..ab0ccce 100644
--- a/src/keycardai_api/resources/organizations/invitations.py
+++ b/src/keycardai_api/resources/organizations/invitations.py
@@ -8,7 +8,7 @@
import httpx
from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given
-from ..._utils import maybe_transform, strip_not_given, async_maybe_transform
+from ..._utils import path_template, maybe_transform, strip_not_given, async_maybe_transform
from ..._compat import cached_property
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import (
@@ -82,7 +82,7 @@ def create(
raise ValueError(f"Expected a non-empty value for `organization_id` but received {organization_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return self._post(
- f"/organizations/{organization_id}/invitations",
+ path_template("/organizations/{organization_id}/invitations", organization_id=organization_id),
body=maybe_transform(
{
"email": email,
@@ -102,7 +102,7 @@ def list(
*,
after: str | Omit = omit,
before: str | Omit = omit,
- expand: List[Literal["permissions"]] | Omit = omit,
+ expand: List[Literal["permissions", "total_count"]] | Omit = omit,
limit: int | 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.
@@ -122,8 +122,11 @@ def list(
before: Cursor for backward pagination
- expand: Fields to expand in the response. Currently supports "permissions" to include
- the permissions field with the caller's permissions for the resource.
+ expand: Fields to expand in the response. Supports "permissions" to include the
+ permissions field with the caller's permissions for the resource. For list
+ organization identities only, "total_count" populates pagination.total_count
+ with the number of identities matching the same filters as the list (excluding
+ cursor and limit). Other operations ignore expand values they do not use.
limit: Maximum number of invitations to return
@@ -139,7 +142,7 @@ def list(
raise ValueError(f"Expected a non-empty value for `organization_id` but received {organization_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return self._get(
- f"/organizations/{organization_id}/invitations",
+ path_template("/organizations/{organization_id}/invitations", organization_id=organization_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -194,7 +197,11 @@ def delete(
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return self._delete(
- f"/organizations/{organization_id}/invitations/{invitation_id}",
+ path_template(
+ "/organizations/{organization_id}/invitations/{invitation_id}",
+ organization_id=organization_id,
+ invitation_id=invitation_id,
+ ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -258,7 +265,7 @@ async def create(
raise ValueError(f"Expected a non-empty value for `organization_id` but received {organization_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return await self._post(
- f"/organizations/{organization_id}/invitations",
+ path_template("/organizations/{organization_id}/invitations", organization_id=organization_id),
body=await async_maybe_transform(
{
"email": email,
@@ -278,7 +285,7 @@ async def list(
*,
after: str | Omit = omit,
before: str | Omit = omit,
- expand: List[Literal["permissions"]] | Omit = omit,
+ expand: List[Literal["permissions", "total_count"]] | Omit = omit,
limit: int | 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.
@@ -298,8 +305,11 @@ async def list(
before: Cursor for backward pagination
- expand: Fields to expand in the response. Currently supports "permissions" to include
- the permissions field with the caller's permissions for the resource.
+ expand: Fields to expand in the response. Supports "permissions" to include the
+ permissions field with the caller's permissions for the resource. For list
+ organization identities only, "total_count" populates pagination.total_count
+ with the number of identities matching the same filters as the list (excluding
+ cursor and limit). Other operations ignore expand values they do not use.
limit: Maximum number of invitations to return
@@ -315,7 +325,7 @@ async def list(
raise ValueError(f"Expected a non-empty value for `organization_id` but received {organization_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return await self._get(
- f"/organizations/{organization_id}/invitations",
+ path_template("/organizations/{organization_id}/invitations", organization_id=organization_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -370,7 +380,11 @@ async def delete(
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return await self._delete(
- f"/organizations/{organization_id}/invitations/{invitation_id}",
+ path_template(
+ "/organizations/{organization_id}/invitations/{invitation_id}",
+ organization_id=organization_id,
+ invitation_id=invitation_id,
+ ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
diff --git a/src/keycardai_api/resources/organizations/organizations.py b/src/keycardai_api/resources/organizations/organizations.py
index 4678241..49df542 100644
--- a/src/keycardai_api/resources/organizations/organizations.py
+++ b/src/keycardai_api/resources/organizations/organizations.py
@@ -25,7 +25,7 @@
organization_list_identities_params,
)
from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
-from ..._utils import maybe_transform, strip_not_given, async_maybe_transform
+from ..._utils import path_template, maybe_transform, strip_not_given, async_maybe_transform
from ..._compat import cached_property
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import (
@@ -145,7 +145,7 @@ def retrieve(
self,
organization_id: str,
*,
- expand: List[Literal["permissions"]] | Omit = omit,
+ expand: List[Literal["permissions", "total_count"]] | 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.
@@ -160,8 +160,11 @@ def retrieve(
Args:
organization_id: Organization ID or label identifier
- expand: Fields to expand in the response. Currently supports "permissions" to include
- the permissions field with the caller's permissions for the resource.
+ expand: Fields to expand in the response. Supports "permissions" to include the
+ permissions field with the caller's permissions for the resource. For list
+ organization identities only, "total_count" populates pagination.total_count
+ with the number of identities matching the same filters as the list (excluding
+ cursor and limit). Other operations ignore expand values they do not use.
extra_headers: Send extra headers
@@ -175,7 +178,7 @@ def retrieve(
raise ValueError(f"Expected a non-empty value for `organization_id` but received {organization_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return self._get(
- f"/organizations/{organization_id}",
+ path_template("/organizations/{organization_id}", organization_id=organization_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -219,7 +222,7 @@ def update(
raise ValueError(f"Expected a non-empty value for `organization_id` but received {organization_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return self._patch(
- f"/organizations/{organization_id}",
+ path_template("/organizations/{organization_id}", organization_id=organization_id),
body=maybe_transform({"name": name}, organization_update_params.OrganizationUpdateParams),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
@@ -232,7 +235,7 @@ def list(
*,
after: str | Omit = omit,
before: str | Omit = omit,
- expand: List[Literal["permissions"]] | Omit = omit,
+ expand: List[Literal["permissions", "total_count"]] | Omit = omit,
limit: int | 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.
@@ -250,8 +253,11 @@ def list(
before: Cursor for backward pagination
- expand: Fields to expand in the response. Currently supports "permissions" to include
- the permissions field with the caller's permissions for the resource.
+ expand: Fields to expand in the response. Supports "permissions" to include the
+ permissions field with the caller's permissions for the resource. For list
+ organization identities only, "total_count" populates pagination.total_count
+ with the number of identities matching the same filters as the list (excluding
+ cursor and limit). Other operations ignore expand values they do not use.
limit: Maximum number of organizations to return
@@ -314,7 +320,7 @@ def exchange_token(
raise ValueError(f"Expected a non-empty value for `organization_id` but received {organization_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return self._post(
- f"/organizations/{organization_id}/token",
+ path_template("/organizations/{organization_id}/token", organization_id=organization_id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -327,8 +333,9 @@ def list_identities(
*,
after: str | Omit = omit,
before: str | Omit = omit,
- expand: List[Literal["permissions"]] | Omit = omit,
+ expand: List[Literal["permissions", "total_count"]] | Omit = omit,
limit: int | Omit = omit,
+ query_email: str | Omit = omit,
role: OrganizationRole | 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.
@@ -348,11 +355,16 @@ def list_identities(
before: Cursor for backward pagination
- expand: Fields to expand in the response. Currently supports "permissions" to include
- the permissions field with the caller's permissions for the resource.
+ expand: Fields to expand in the response. Supports "permissions" to include the
+ permissions field with the caller's permissions for the resource. For list
+ organization identities only, "total_count" populates pagination.total_count
+ with the number of identities matching the same filters as the list (excluding
+ cursor and limit). Other operations ignore expand values they do not use.
limit: Maximum number of identities to return
+ query_email: Search identities by email substring (case-insensitive)
+
role: Filter identities by role
extra_headers: Send extra headers
@@ -367,7 +379,7 @@ def list_identities(
raise ValueError(f"Expected a non-empty value for `organization_id` but received {organization_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return self._get(
- f"/organizations/{organization_id}/identities",
+ path_template("/organizations/{organization_id}/identities", organization_id=organization_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -379,6 +391,7 @@ def list_identities(
"before": before,
"expand": expand,
"limit": limit,
+ "query_email": query_email,
"role": role,
},
organization_list_identities_params.OrganizationListIdentitiesParams,
@@ -391,7 +404,7 @@ def list_roles(
self,
organization_id: str,
*,
- expand: List[Literal["permissions"]] | Omit = omit,
+ expand: List[Literal["permissions", "total_count"]] | Omit = omit,
scope: RoleScope | 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.
@@ -416,8 +429,11 @@ def list_roles(
Args:
organization_id: Organization ID or label identifier
- expand: Fields to expand in the response. Currently supports "permissions" to include
- the permissions field with the caller's permissions for the resource.
+ expand: Fields to expand in the response. Supports "permissions" to include the
+ permissions field with the caller's permissions for the resource. For list
+ organization identities only, "total_count" populates pagination.total_count
+ with the number of identities matching the same filters as the list (excluding
+ cursor and limit). Other operations ignore expand values they do not use.
scope: Filter roles by scope (organization or zone level)
@@ -433,7 +449,7 @@ def list_roles(
raise ValueError(f"Expected a non-empty value for `organization_id` but received {organization_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return self._get(
- f"/organizations/{organization_id}/roles",
+ path_template("/organizations/{organization_id}/roles", organization_id=organization_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -525,7 +541,7 @@ async def retrieve(
self,
organization_id: str,
*,
- expand: List[Literal["permissions"]] | Omit = omit,
+ expand: List[Literal["permissions", "total_count"]] | 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.
@@ -540,8 +556,11 @@ async def retrieve(
Args:
organization_id: Organization ID or label identifier
- expand: Fields to expand in the response. Currently supports "permissions" to include
- the permissions field with the caller's permissions for the resource.
+ expand: Fields to expand in the response. Supports "permissions" to include the
+ permissions field with the caller's permissions for the resource. For list
+ organization identities only, "total_count" populates pagination.total_count
+ with the number of identities matching the same filters as the list (excluding
+ cursor and limit). Other operations ignore expand values they do not use.
extra_headers: Send extra headers
@@ -555,7 +574,7 @@ async def retrieve(
raise ValueError(f"Expected a non-empty value for `organization_id` but received {organization_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return await self._get(
- f"/organizations/{organization_id}",
+ path_template("/organizations/{organization_id}", organization_id=organization_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -601,7 +620,7 @@ async def update(
raise ValueError(f"Expected a non-empty value for `organization_id` but received {organization_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return await self._patch(
- f"/organizations/{organization_id}",
+ path_template("/organizations/{organization_id}", organization_id=organization_id),
body=await async_maybe_transform({"name": name}, organization_update_params.OrganizationUpdateParams),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
@@ -614,7 +633,7 @@ async def list(
*,
after: str | Omit = omit,
before: str | Omit = omit,
- expand: List[Literal["permissions"]] | Omit = omit,
+ expand: List[Literal["permissions", "total_count"]] | Omit = omit,
limit: int | 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.
@@ -632,8 +651,11 @@ async def list(
before: Cursor for backward pagination
- expand: Fields to expand in the response. Currently supports "permissions" to include
- the permissions field with the caller's permissions for the resource.
+ expand: Fields to expand in the response. Supports "permissions" to include the
+ permissions field with the caller's permissions for the resource. For list
+ organization identities only, "total_count" populates pagination.total_count
+ with the number of identities matching the same filters as the list (excluding
+ cursor and limit). Other operations ignore expand values they do not use.
limit: Maximum number of organizations to return
@@ -696,7 +718,7 @@ async def exchange_token(
raise ValueError(f"Expected a non-empty value for `organization_id` but received {organization_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return await self._post(
- f"/organizations/{organization_id}/token",
+ path_template("/organizations/{organization_id}/token", organization_id=organization_id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -709,8 +731,9 @@ async def list_identities(
*,
after: str | Omit = omit,
before: str | Omit = omit,
- expand: List[Literal["permissions"]] | Omit = omit,
+ expand: List[Literal["permissions", "total_count"]] | Omit = omit,
limit: int | Omit = omit,
+ query_email: str | Omit = omit,
role: OrganizationRole | 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.
@@ -730,11 +753,16 @@ async def list_identities(
before: Cursor for backward pagination
- expand: Fields to expand in the response. Currently supports "permissions" to include
- the permissions field with the caller's permissions for the resource.
+ expand: Fields to expand in the response. Supports "permissions" to include the
+ permissions field with the caller's permissions for the resource. For list
+ organization identities only, "total_count" populates pagination.total_count
+ with the number of identities matching the same filters as the list (excluding
+ cursor and limit). Other operations ignore expand values they do not use.
limit: Maximum number of identities to return
+ query_email: Search identities by email substring (case-insensitive)
+
role: Filter identities by role
extra_headers: Send extra headers
@@ -749,7 +777,7 @@ async def list_identities(
raise ValueError(f"Expected a non-empty value for `organization_id` but received {organization_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return await self._get(
- f"/organizations/{organization_id}/identities",
+ path_template("/organizations/{organization_id}/identities", organization_id=organization_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -761,6 +789,7 @@ async def list_identities(
"before": before,
"expand": expand,
"limit": limit,
+ "query_email": query_email,
"role": role,
},
organization_list_identities_params.OrganizationListIdentitiesParams,
@@ -773,7 +802,7 @@ async def list_roles(
self,
organization_id: str,
*,
- expand: List[Literal["permissions"]] | Omit = omit,
+ expand: List[Literal["permissions", "total_count"]] | Omit = omit,
scope: RoleScope | 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.
@@ -798,8 +827,11 @@ async def list_roles(
Args:
organization_id: Organization ID or label identifier
- expand: Fields to expand in the response. Currently supports "permissions" to include
- the permissions field with the caller's permissions for the resource.
+ expand: Fields to expand in the response. Supports "permissions" to include the
+ permissions field with the caller's permissions for the resource. For list
+ organization identities only, "total_count" populates pagination.total_count
+ with the number of identities matching the same filters as the list (excluding
+ cursor and limit). Other operations ignore expand values they do not use.
scope: Filter roles by scope (organization or zone level)
@@ -815,7 +847,7 @@ async def list_roles(
raise ValueError(f"Expected a non-empty value for `organization_id` but received {organization_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return await self._get(
- f"/organizations/{organization_id}/roles",
+ path_template("/organizations/{organization_id}/roles", organization_id=organization_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
diff --git a/src/keycardai_api/resources/organizations/service_accounts/credentials.py b/src/keycardai_api/resources/organizations/service_accounts/credentials.py
index 5b5e4fe..e74e94a 100644
--- a/src/keycardai_api/resources/organizations/service_accounts/credentials.py
+++ b/src/keycardai_api/resources/organizations/service_accounts/credentials.py
@@ -8,7 +8,7 @@
import httpx
from ...._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given
-from ...._utils import maybe_transform, strip_not_given, async_maybe_transform
+from ...._utils import path_template, maybe_transform, strip_not_given, async_maybe_transform
from ...._compat import cached_property
from ...._resource import SyncAPIResource, AsyncAPIResource
from ...._response import (
@@ -92,7 +92,11 @@ def create(
raise ValueError(f"Expected a non-empty value for `service_account_id` but received {service_account_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return self._post(
- f"/organizations/{organization_id}/service-accounts/{service_account_id}/credentials",
+ path_template(
+ "/organizations/{organization_id}/service-accounts/{service_account_id}/credentials",
+ organization_id=organization_id,
+ service_account_id=service_account_id,
+ ),
body=maybe_transform(
{
"name": name,
@@ -112,7 +116,7 @@ def retrieve(
*,
organization_id: str,
service_account_id: str,
- expand: List[Literal["permissions"]] | Omit = omit,
+ expand: List[Literal["permissions", "total_count"]] | 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.
@@ -131,8 +135,11 @@ def retrieve(
credential_id: Identifier for API resources. A 26-char nanoid (URL/DNS safe).
- expand: Fields to expand in the response. Currently supports "permissions" to include
- the permissions field with the caller's permissions for the resource.
+ expand: Fields to expand in the response. Supports "permissions" to include the
+ permissions field with the caller's permissions for the resource. For list
+ organization identities only, "total_count" populates pagination.total_count
+ with the number of identities matching the same filters as the list (excluding
+ cursor and limit). Other operations ignore expand values they do not use.
extra_headers: Send extra headers
@@ -150,7 +157,12 @@ def retrieve(
raise ValueError(f"Expected a non-empty value for `credential_id` but received {credential_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return self._get(
- f"/organizations/{organization_id}/service-accounts/{service_account_id}/credentials/{credential_id}",
+ path_template(
+ "/organizations/{organization_id}/service-accounts/{service_account_id}/credentials/{credential_id}",
+ organization_id=organization_id,
+ service_account_id=service_account_id,
+ credential_id=credential_id,
+ ),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -207,7 +219,12 @@ def update(
raise ValueError(f"Expected a non-empty value for `credential_id` but received {credential_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return self._patch(
- f"/organizations/{organization_id}/service-accounts/{service_account_id}/credentials/{credential_id}",
+ path_template(
+ "/organizations/{organization_id}/service-accounts/{service_account_id}/credentials/{credential_id}",
+ organization_id=organization_id,
+ service_account_id=service_account_id,
+ credential_id=credential_id,
+ ),
body=maybe_transform(
{
"description": description,
@@ -228,7 +245,7 @@ def list(
organization_id: str,
after: str | Omit = omit,
before: str | Omit = omit,
- expand: List[Literal["permissions"]] | Omit = omit,
+ expand: List[Literal["permissions", "total_count"]] | Omit = omit,
limit: int | 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.
@@ -250,8 +267,11 @@ def list(
before: Cursor for backward pagination
- expand: Fields to expand in the response. Currently supports "permissions" to include
- the permissions field with the caller's permissions for the resource.
+ expand: Fields to expand in the response. Supports "permissions" to include the
+ permissions field with the caller's permissions for the resource. For list
+ organization identities only, "total_count" populates pagination.total_count
+ with the number of identities matching the same filters as the list (excluding
+ cursor and limit). Other operations ignore expand values they do not use.
limit: Maximum number of credentials to return
@@ -269,7 +289,11 @@ def list(
raise ValueError(f"Expected a non-empty value for `service_account_id` but received {service_account_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return self._get(
- f"/organizations/{organization_id}/service-accounts/{service_account_id}/credentials",
+ path_template(
+ "/organizations/{organization_id}/service-accounts/{service_account_id}/credentials",
+ organization_id=organization_id,
+ service_account_id=service_account_id,
+ ),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -329,7 +353,12 @@ def delete(
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return self._delete(
- f"/organizations/{organization_id}/service-accounts/{service_account_id}/credentials/{credential_id}",
+ path_template(
+ "/organizations/{organization_id}/service-accounts/{service_account_id}/credentials/{credential_id}",
+ organization_id=organization_id,
+ service_account_id=service_account_id,
+ credential_id=credential_id,
+ ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -398,7 +427,11 @@ async def create(
raise ValueError(f"Expected a non-empty value for `service_account_id` but received {service_account_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return await self._post(
- f"/organizations/{organization_id}/service-accounts/{service_account_id}/credentials",
+ path_template(
+ "/organizations/{organization_id}/service-accounts/{service_account_id}/credentials",
+ organization_id=organization_id,
+ service_account_id=service_account_id,
+ ),
body=await async_maybe_transform(
{
"name": name,
@@ -418,7 +451,7 @@ async def retrieve(
*,
organization_id: str,
service_account_id: str,
- expand: List[Literal["permissions"]] | Omit = omit,
+ expand: List[Literal["permissions", "total_count"]] | 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.
@@ -437,8 +470,11 @@ async def retrieve(
credential_id: Identifier for API resources. A 26-char nanoid (URL/DNS safe).
- expand: Fields to expand in the response. Currently supports "permissions" to include
- the permissions field with the caller's permissions for the resource.
+ expand: Fields to expand in the response. Supports "permissions" to include the
+ permissions field with the caller's permissions for the resource. For list
+ organization identities only, "total_count" populates pagination.total_count
+ with the number of identities matching the same filters as the list (excluding
+ cursor and limit). Other operations ignore expand values they do not use.
extra_headers: Send extra headers
@@ -456,7 +492,12 @@ async def retrieve(
raise ValueError(f"Expected a non-empty value for `credential_id` but received {credential_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return await self._get(
- f"/organizations/{organization_id}/service-accounts/{service_account_id}/credentials/{credential_id}",
+ path_template(
+ "/organizations/{organization_id}/service-accounts/{service_account_id}/credentials/{credential_id}",
+ organization_id=organization_id,
+ service_account_id=service_account_id,
+ credential_id=credential_id,
+ ),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -515,7 +556,12 @@ async def update(
raise ValueError(f"Expected a non-empty value for `credential_id` but received {credential_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return await self._patch(
- f"/organizations/{organization_id}/service-accounts/{service_account_id}/credentials/{credential_id}",
+ path_template(
+ "/organizations/{organization_id}/service-accounts/{service_account_id}/credentials/{credential_id}",
+ organization_id=organization_id,
+ service_account_id=service_account_id,
+ credential_id=credential_id,
+ ),
body=await async_maybe_transform(
{
"description": description,
@@ -536,7 +582,7 @@ async def list(
organization_id: str,
after: str | Omit = omit,
before: str | Omit = omit,
- expand: List[Literal["permissions"]] | Omit = omit,
+ expand: List[Literal["permissions", "total_count"]] | Omit = omit,
limit: int | 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.
@@ -558,8 +604,11 @@ async def list(
before: Cursor for backward pagination
- expand: Fields to expand in the response. Currently supports "permissions" to include
- the permissions field with the caller's permissions for the resource.
+ expand: Fields to expand in the response. Supports "permissions" to include the
+ permissions field with the caller's permissions for the resource. For list
+ organization identities only, "total_count" populates pagination.total_count
+ with the number of identities matching the same filters as the list (excluding
+ cursor and limit). Other operations ignore expand values they do not use.
limit: Maximum number of credentials to return
@@ -577,7 +626,11 @@ async def list(
raise ValueError(f"Expected a non-empty value for `service_account_id` but received {service_account_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return await self._get(
- f"/organizations/{organization_id}/service-accounts/{service_account_id}/credentials",
+ path_template(
+ "/organizations/{organization_id}/service-accounts/{service_account_id}/credentials",
+ organization_id=organization_id,
+ service_account_id=service_account_id,
+ ),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -637,7 +690,12 @@ async def delete(
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return await self._delete(
- f"/organizations/{organization_id}/service-accounts/{service_account_id}/credentials/{credential_id}",
+ path_template(
+ "/organizations/{organization_id}/service-accounts/{service_account_id}/credentials/{credential_id}",
+ organization_id=organization_id,
+ service_account_id=service_account_id,
+ credential_id=credential_id,
+ ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
diff --git a/src/keycardai_api/resources/organizations/service_accounts/service_accounts.py b/src/keycardai_api/resources/organizations/service_accounts/service_accounts.py
index 5817d28..68715b3 100644
--- a/src/keycardai_api/resources/organizations/service_accounts/service_accounts.py
+++ b/src/keycardai_api/resources/organizations/service_accounts/service_accounts.py
@@ -8,7 +8,7 @@
import httpx
from ...._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given
-from ...._utils import maybe_transform, strip_not_given, async_maybe_transform
+from ...._utils import path_template, maybe_transform, strip_not_given, async_maybe_transform
from ...._compat import cached_property
from .credentials import (
CredentialsResource,
@@ -98,7 +98,7 @@ def create(
raise ValueError(f"Expected a non-empty value for `organization_id` but received {organization_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return self._post(
- f"/organizations/{organization_id}/service-accounts",
+ path_template("/organizations/{organization_id}/service-accounts", organization_id=organization_id),
body=maybe_transform(
{
"name": name,
@@ -117,7 +117,7 @@ def retrieve(
service_account_id: str,
*,
organization_id: str,
- expand: List[Literal["permissions"]] | Omit = omit,
+ expand: List[Literal["permissions", "total_count"]] | 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.
@@ -134,8 +134,11 @@ def retrieve(
service_account_id: Identifier for API resources. A 26-char nanoid (URL/DNS safe).
- expand: Fields to expand in the response. Currently supports "permissions" to include
- the permissions field with the caller's permissions for the resource.
+ expand: Fields to expand in the response. Supports "permissions" to include the
+ permissions field with the caller's permissions for the resource. For list
+ organization identities only, "total_count" populates pagination.total_count
+ with the number of identities matching the same filters as the list (excluding
+ cursor and limit). Other operations ignore expand values they do not use.
extra_headers: Send extra headers
@@ -151,7 +154,11 @@ def retrieve(
raise ValueError(f"Expected a non-empty value for `service_account_id` but received {service_account_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return self._get(
- f"/organizations/{organization_id}/service-accounts/{service_account_id}",
+ path_template(
+ "/organizations/{organization_id}/service-accounts/{service_account_id}",
+ organization_id=organization_id,
+ service_account_id=service_account_id,
+ ),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -203,7 +210,11 @@ def update(
raise ValueError(f"Expected a non-empty value for `service_account_id` but received {service_account_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return self._patch(
- f"/organizations/{organization_id}/service-accounts/{service_account_id}",
+ path_template(
+ "/organizations/{organization_id}/service-accounts/{service_account_id}",
+ organization_id=organization_id,
+ service_account_id=service_account_id,
+ ),
body=maybe_transform(
{
"description": description,
@@ -223,7 +234,7 @@ def list(
*,
after: str | Omit = omit,
before: str | Omit = omit,
- expand: List[Literal["permissions"]] | Omit = omit,
+ expand: List[Literal["permissions", "total_count"]] | Omit = omit,
limit: int | 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.
@@ -243,8 +254,11 @@ def list(
before: Cursor for backward pagination
- expand: Fields to expand in the response. Currently supports "permissions" to include
- the permissions field with the caller's permissions for the resource.
+ expand: Fields to expand in the response. Supports "permissions" to include the
+ permissions field with the caller's permissions for the resource. For list
+ organization identities only, "total_count" populates pagination.total_count
+ with the number of identities matching the same filters as the list (excluding
+ cursor and limit). Other operations ignore expand values they do not use.
limit: Maximum number of service accounts to return
@@ -260,7 +274,7 @@ def list(
raise ValueError(f"Expected a non-empty value for `organization_id` but received {organization_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return self._get(
- f"/organizations/{organization_id}/service-accounts",
+ path_template("/organizations/{organization_id}/service-accounts", organization_id=organization_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -315,7 +329,11 @@ def delete(
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return self._delete(
- f"/organizations/{organization_id}/service-accounts/{service_account_id}",
+ path_template(
+ "/organizations/{organization_id}/service-accounts/{service_account_id}",
+ organization_id=organization_id,
+ service_account_id=service_account_id,
+ ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -383,7 +401,7 @@ async def create(
raise ValueError(f"Expected a non-empty value for `organization_id` but received {organization_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return await self._post(
- f"/organizations/{organization_id}/service-accounts",
+ path_template("/organizations/{organization_id}/service-accounts", organization_id=organization_id),
body=await async_maybe_transform(
{
"name": name,
@@ -402,7 +420,7 @@ async def retrieve(
service_account_id: str,
*,
organization_id: str,
- expand: List[Literal["permissions"]] | Omit = omit,
+ expand: List[Literal["permissions", "total_count"]] | 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.
@@ -419,8 +437,11 @@ async def retrieve(
service_account_id: Identifier for API resources. A 26-char nanoid (URL/DNS safe).
- expand: Fields to expand in the response. Currently supports "permissions" to include
- the permissions field with the caller's permissions for the resource.
+ expand: Fields to expand in the response. Supports "permissions" to include the
+ permissions field with the caller's permissions for the resource. For list
+ organization identities only, "total_count" populates pagination.total_count
+ with the number of identities matching the same filters as the list (excluding
+ cursor and limit). Other operations ignore expand values they do not use.
extra_headers: Send extra headers
@@ -436,7 +457,11 @@ async def retrieve(
raise ValueError(f"Expected a non-empty value for `service_account_id` but received {service_account_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return await self._get(
- f"/organizations/{organization_id}/service-accounts/{service_account_id}",
+ path_template(
+ "/organizations/{organization_id}/service-accounts/{service_account_id}",
+ organization_id=organization_id,
+ service_account_id=service_account_id,
+ ),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -490,7 +515,11 @@ async def update(
raise ValueError(f"Expected a non-empty value for `service_account_id` but received {service_account_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return await self._patch(
- f"/organizations/{organization_id}/service-accounts/{service_account_id}",
+ path_template(
+ "/organizations/{organization_id}/service-accounts/{service_account_id}",
+ organization_id=organization_id,
+ service_account_id=service_account_id,
+ ),
body=await async_maybe_transform(
{
"description": description,
@@ -510,7 +539,7 @@ async def list(
*,
after: str | Omit = omit,
before: str | Omit = omit,
- expand: List[Literal["permissions"]] | Omit = omit,
+ expand: List[Literal["permissions", "total_count"]] | Omit = omit,
limit: int | 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.
@@ -530,8 +559,11 @@ async def list(
before: Cursor for backward pagination
- expand: Fields to expand in the response. Currently supports "permissions" to include
- the permissions field with the caller's permissions for the resource.
+ expand: Fields to expand in the response. Supports "permissions" to include the
+ permissions field with the caller's permissions for the resource. For list
+ organization identities only, "total_count" populates pagination.total_count
+ with the number of identities matching the same filters as the list (excluding
+ cursor and limit). Other operations ignore expand values they do not use.
limit: Maximum number of service accounts to return
@@ -547,7 +579,7 @@ async def list(
raise ValueError(f"Expected a non-empty value for `organization_id` but received {organization_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return await self._get(
- f"/organizations/{organization_id}/service-accounts",
+ path_template("/organizations/{organization_id}/service-accounts", organization_id=organization_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -602,7 +634,11 @@ async def delete(
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return await self._delete(
- f"/organizations/{organization_id}/service-accounts/{service_account_id}",
+ path_template(
+ "/organizations/{organization_id}/service-accounts/{service_account_id}",
+ organization_id=organization_id,
+ service_account_id=service_account_id,
+ ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
diff --git a/src/keycardai_api/resources/organizations/sso_connection.py b/src/keycardai_api/resources/organizations/sso_connection.py
index 7c4ddac..4a8a44f 100644
--- a/src/keycardai_api/resources/organizations/sso_connection.py
+++ b/src/keycardai_api/resources/organizations/sso_connection.py
@@ -8,7 +8,7 @@
import httpx
from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given
-from ..._utils import maybe_transform, strip_not_given, async_maybe_transform
+from ..._utils import path_template, maybe_transform, strip_not_given, async_maybe_transform
from ..._compat import cached_property
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import (
@@ -53,7 +53,7 @@ def retrieve(
self,
organization_id: str,
*,
- expand: List[Literal["permissions"]] | Omit = omit,
+ expand: List[Literal["permissions", "total_count"]] | 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.
@@ -68,8 +68,11 @@ def retrieve(
Args:
organization_id: Organization ID or label identifier
- expand: Fields to expand in the response. Currently supports "permissions" to include
- the permissions field with the caller's permissions for the resource.
+ expand: Fields to expand in the response. Supports "permissions" to include the
+ permissions field with the caller's permissions for the resource. For list
+ organization identities only, "total_count" populates pagination.total_count
+ with the number of identities matching the same filters as the list (excluding
+ cursor and limit). Other operations ignore expand values they do not use.
extra_headers: Send extra headers
@@ -83,7 +86,7 @@ def retrieve(
raise ValueError(f"Expected a non-empty value for `organization_id` but received {organization_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return self._get(
- f"/organizations/{organization_id}/sso-connection",
+ path_template("/organizations/{organization_id}/sso-connection", organization_id=organization_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -136,7 +139,7 @@ def update(
raise ValueError(f"Expected a non-empty value for `organization_id` but received {organization_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return self._patch(
- f"/organizations/{organization_id}/sso-connection",
+ path_template("/organizations/{organization_id}/sso-connection", organization_id=organization_id),
body=maybe_transform(
{
"client_id": client_id,
@@ -183,7 +186,7 @@ def disable(
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return self._delete(
- f"/organizations/{organization_id}/sso-connection",
+ path_template("/organizations/{organization_id}/sso-connection", organization_id=organization_id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -232,7 +235,7 @@ def enable(
raise ValueError(f"Expected a non-empty value for `organization_id` but received {organization_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return self._post(
- f"/organizations/{organization_id}/sso-connection",
+ path_template("/organizations/{organization_id}/sso-connection", organization_id=organization_id),
body=maybe_transform(
{
"client_id": client_id,
@@ -273,7 +276,7 @@ async def retrieve(
self,
organization_id: str,
*,
- expand: List[Literal["permissions"]] | Omit = omit,
+ expand: List[Literal["permissions", "total_count"]] | 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.
@@ -288,8 +291,11 @@ async def retrieve(
Args:
organization_id: Organization ID or label identifier
- expand: Fields to expand in the response. Currently supports "permissions" to include
- the permissions field with the caller's permissions for the resource.
+ expand: Fields to expand in the response. Supports "permissions" to include the
+ permissions field with the caller's permissions for the resource. For list
+ organization identities only, "total_count" populates pagination.total_count
+ with the number of identities matching the same filters as the list (excluding
+ cursor and limit). Other operations ignore expand values they do not use.
extra_headers: Send extra headers
@@ -303,7 +309,7 @@ async def retrieve(
raise ValueError(f"Expected a non-empty value for `organization_id` but received {organization_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return await self._get(
- f"/organizations/{organization_id}/sso-connection",
+ path_template("/organizations/{organization_id}/sso-connection", organization_id=organization_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -358,7 +364,7 @@ async def update(
raise ValueError(f"Expected a non-empty value for `organization_id` but received {organization_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return await self._patch(
- f"/organizations/{organization_id}/sso-connection",
+ path_template("/organizations/{organization_id}/sso-connection", organization_id=organization_id),
body=await async_maybe_transform(
{
"client_id": client_id,
@@ -405,7 +411,7 @@ async def disable(
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return await self._delete(
- f"/organizations/{organization_id}/sso-connection",
+ path_template("/organizations/{organization_id}/sso-connection", organization_id=organization_id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -454,7 +460,7 @@ async def enable(
raise ValueError(f"Expected a non-empty value for `organization_id` but received {organization_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return await self._post(
- f"/organizations/{organization_id}/sso-connection",
+ path_template("/organizations/{organization_id}/sso-connection", organization_id=organization_id),
body=await async_maybe_transform(
{
"client_id": client_id,
diff --git a/src/keycardai_api/resources/organizations/users.py b/src/keycardai_api/resources/organizations/users.py
index 29e0b12..9ef4fa1 100644
--- a/src/keycardai_api/resources/organizations/users.py
+++ b/src/keycardai_api/resources/organizations/users.py
@@ -8,7 +8,7 @@
import httpx
from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given
-from ..._utils import maybe_transform, strip_not_given, async_maybe_transform
+from ..._utils import path_template, maybe_transform, strip_not_given, async_maybe_transform
from ..._compat import cached_property
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import (
@@ -58,7 +58,7 @@ def retrieve(
user_id: str,
*,
organization_id: str,
- expand: List[Literal["permissions"]] | Omit = omit,
+ expand: List[Literal["permissions", "total_count"]] | 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.
@@ -75,8 +75,11 @@ def retrieve(
user_id: Identifier for API resources. A 26-char nanoid (URL/DNS safe).
- expand: Fields to expand in the response. Currently supports "permissions" to include
- the permissions field with the caller's permissions for the resource.
+ expand: Fields to expand in the response. Supports "permissions" to include the
+ permissions field with the caller's permissions for the resource. For list
+ organization identities only, "total_count" populates pagination.total_count
+ with the number of identities matching the same filters as the list (excluding
+ cursor and limit). Other operations ignore expand values they do not use.
extra_headers: Send extra headers
@@ -92,7 +95,9 @@ def retrieve(
raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return self._get(
- f"/organizations/{organization_id}/users/{user_id}",
+ path_template(
+ "/organizations/{organization_id}/users/{user_id}", organization_id=organization_id, user_id=user_id
+ ),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -144,7 +149,9 @@ def update(
raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return self._patch(
- f"/organizations/{organization_id}/users/{user_id}",
+ path_template(
+ "/organizations/{organization_id}/users/{user_id}", organization_id=organization_id, user_id=user_id
+ ),
body=maybe_transform(
{
"role": role,
@@ -164,7 +171,7 @@ def list(
*,
after: str | Omit = omit,
before: str | Omit = omit,
- expand: List[Literal["permissions"]] | Omit = omit,
+ expand: List[Literal["permissions", "total_count"]] | Omit = omit,
limit: int | Omit = omit,
role: OrganizationRole | Omit = omit,
x_client_request_id: str | Omit = omit,
@@ -185,8 +192,11 @@ def list(
before: Cursor for backward pagination
- expand: Fields to expand in the response. Currently supports "permissions" to include
- the permissions field with the caller's permissions for the resource.
+ expand: Fields to expand in the response. Supports "permissions" to include the
+ permissions field with the caller's permissions for the resource. For list
+ organization identities only, "total_count" populates pagination.total_count
+ with the number of identities matching the same filters as the list (excluding
+ cursor and limit). Other operations ignore expand values they do not use.
limit: Maximum number of users to return
@@ -204,7 +214,7 @@ def list(
raise ValueError(f"Expected a non-empty value for `organization_id` but received {organization_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return self._get(
- f"/organizations/{organization_id}/users",
+ path_template("/organizations/{organization_id}/users", organization_id=organization_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -260,7 +270,9 @@ def delete(
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return self._delete(
- f"/organizations/{organization_id}/users/{user_id}",
+ path_template(
+ "/organizations/{organization_id}/users/{user_id}", organization_id=organization_id, user_id=user_id
+ ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -293,7 +305,7 @@ async def retrieve(
user_id: str,
*,
organization_id: str,
- expand: List[Literal["permissions"]] | Omit = omit,
+ expand: List[Literal["permissions", "total_count"]] | 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.
@@ -310,8 +322,11 @@ async def retrieve(
user_id: Identifier for API resources. A 26-char nanoid (URL/DNS safe).
- expand: Fields to expand in the response. Currently supports "permissions" to include
- the permissions field with the caller's permissions for the resource.
+ expand: Fields to expand in the response. Supports "permissions" to include the
+ permissions field with the caller's permissions for the resource. For list
+ organization identities only, "total_count" populates pagination.total_count
+ with the number of identities matching the same filters as the list (excluding
+ cursor and limit). Other operations ignore expand values they do not use.
extra_headers: Send extra headers
@@ -327,7 +342,9 @@ async def retrieve(
raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return await self._get(
- f"/organizations/{organization_id}/users/{user_id}",
+ path_template(
+ "/organizations/{organization_id}/users/{user_id}", organization_id=organization_id, user_id=user_id
+ ),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -379,7 +396,9 @@ async def update(
raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return await self._patch(
- f"/organizations/{organization_id}/users/{user_id}",
+ path_template(
+ "/organizations/{organization_id}/users/{user_id}", organization_id=organization_id, user_id=user_id
+ ),
body=await async_maybe_transform(
{
"role": role,
@@ -399,7 +418,7 @@ async def list(
*,
after: str | Omit = omit,
before: str | Omit = omit,
- expand: List[Literal["permissions"]] | Omit = omit,
+ expand: List[Literal["permissions", "total_count"]] | Omit = omit,
limit: int | Omit = omit,
role: OrganizationRole | Omit = omit,
x_client_request_id: str | Omit = omit,
@@ -420,8 +439,11 @@ async def list(
before: Cursor for backward pagination
- expand: Fields to expand in the response. Currently supports "permissions" to include
- the permissions field with the caller's permissions for the resource.
+ expand: Fields to expand in the response. Supports "permissions" to include the
+ permissions field with the caller's permissions for the resource. For list
+ organization identities only, "total_count" populates pagination.total_count
+ with the number of identities matching the same filters as the list (excluding
+ cursor and limit). Other operations ignore expand values they do not use.
limit: Maximum number of users to return
@@ -439,7 +461,7 @@ async def list(
raise ValueError(f"Expected a non-empty value for `organization_id` but received {organization_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return await self._get(
- f"/organizations/{organization_id}/users",
+ path_template("/organizations/{organization_id}/users", organization_id=organization_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -495,7 +517,9 @@ async def delete(
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return await self._delete(
- f"/organizations/{organization_id}/users/{user_id}",
+ path_template(
+ "/organizations/{organization_id}/users/{user_id}", organization_id=organization_id, user_id=user_id
+ ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
diff --git a/src/keycardai_api/resources/zones/application_credentials.py b/src/keycardai_api/resources/zones/application_credentials.py
index 1d2abf7..14e9b18 100644
--- a/src/keycardai_api/resources/zones/application_credentials.py
+++ b/src/keycardai_api/resources/zones/application_credentials.py
@@ -8,7 +8,7 @@
import httpx
from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given
-from ..._utils import required_args, maybe_transform, async_maybe_transform
+from ..._utils import path_template, required_args, maybe_transform, async_maybe_transform
from ..._compat import cached_property
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import (
@@ -253,7 +253,7 @@ def create(
return cast(
ApplicationCredentialCreateResponse,
self._post(
- f"/zones/{zone_id}/application-credentials",
+ path_template("/zones/{zone_id}/application-credentials", zone_id=zone_id),
body=maybe_transform(
{
"application_id": application_id,
@@ -305,7 +305,7 @@ def retrieve(
return cast(
Credential,
self._get(
- f"/zones/{zone_id}/application-credentials/{id}",
+ path_template("/zones/{zone_id}/application-credentials/{id}", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -491,7 +491,7 @@ def update(
return cast(
Credential,
self._patch(
- f"/zones/{zone_id}/application-credentials/{id}",
+ path_template("/zones/{zone_id}/application-credentials/{id}", zone_id=zone_id, id=id),
body=maybe_transform(
{
"subject": subject,
@@ -546,7 +546,7 @@ def list(
if not zone_id:
raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
return self._get(
- f"/zones/{zone_id}/application-credentials",
+ path_template("/zones/{zone_id}/application-credentials", zone_id=zone_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -598,7 +598,7 @@ def delete(
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
return self._delete(
- f"/zones/{zone_id}/application-credentials/{id}",
+ path_template("/zones/{zone_id}/application-credentials/{id}", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -829,7 +829,7 @@ async def create(
return cast(
ApplicationCredentialCreateResponse,
await self._post(
- f"/zones/{zone_id}/application-credentials",
+ path_template("/zones/{zone_id}/application-credentials", zone_id=zone_id),
body=await async_maybe_transform(
{
"application_id": application_id,
@@ -881,7 +881,7 @@ async def retrieve(
return cast(
Credential,
await self._get(
- f"/zones/{zone_id}/application-credentials/{id}",
+ path_template("/zones/{zone_id}/application-credentials/{id}", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -1067,7 +1067,7 @@ async def update(
return cast(
Credential,
await self._patch(
- f"/zones/{zone_id}/application-credentials/{id}",
+ path_template("/zones/{zone_id}/application-credentials/{id}", zone_id=zone_id, id=id),
body=await async_maybe_transform(
{
"subject": subject,
@@ -1122,7 +1122,7 @@ async def list(
if not zone_id:
raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
return await self._get(
- f"/zones/{zone_id}/application-credentials",
+ path_template("/zones/{zone_id}/application-credentials", zone_id=zone_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -1174,7 +1174,7 @@ async def delete(
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
return await self._delete(
- f"/zones/{zone_id}/application-credentials/{id}",
+ path_template("/zones/{zone_id}/application-credentials/{id}", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
diff --git a/src/keycardai_api/resources/zones/applications/applications.py b/src/keycardai_api/resources/zones/applications/applications.py
index b03bbfa..da8b945 100644
--- a/src/keycardai_api/resources/zones/applications/applications.py
+++ b/src/keycardai_api/resources/zones/applications/applications.py
@@ -8,7 +8,7 @@
import httpx
from ...._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given
-from ...._utils import maybe_transform, async_maybe_transform
+from ...._utils import path_template, maybe_transform, async_maybe_transform
from ...._compat import cached_property
from ...._resource import SyncAPIResource, AsyncAPIResource
from ...._response import (
@@ -113,7 +113,7 @@ def create(
if not zone_id:
raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
return self._post(
- f"/zones/{zone_id}/applications",
+ path_template("/zones/{zone_id}/applications", zone_id=zone_id),
body=maybe_transform(
{
"identifier": identifier,
@@ -160,7 +160,7 @@ def retrieve(
if not id:
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
return self._get(
- f"/zones/{zone_id}/applications/{id}",
+ path_template("/zones/{zone_id}/applications/{id}", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -211,7 +211,7 @@ def update(
if not id:
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
return self._patch(
- f"/zones/{zone_id}/applications/{id}",
+ path_template("/zones/{zone_id}/applications/{id}", zone_id=zone_id, id=id),
body=maybe_transform(
{
"description": description,
@@ -275,7 +275,7 @@ def list(
if not zone_id:
raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
return self._get(
- f"/zones/{zone_id}/applications",
+ path_template("/zones/{zone_id}/applications", zone_id=zone_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -329,7 +329,7 @@ def delete(
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
return self._delete(
- f"/zones/{zone_id}/applications/{id}",
+ path_template("/zones/{zone_id}/applications/{id}", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -376,7 +376,7 @@ def list_credentials(
if not id:
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
return self._get(
- f"/zones/{zone_id}/applications/{id}/application-credentials",
+ path_template("/zones/{zone_id}/applications/{id}/application-credentials", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -436,7 +436,7 @@ def list_resources(
if not id:
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
return self._get(
- f"/zones/{zone_id}/applications/{id}/resources",
+ path_template("/zones/{zone_id}/applications/{id}/resources", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -526,7 +526,7 @@ async def create(
if not zone_id:
raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
return await self._post(
- f"/zones/{zone_id}/applications",
+ path_template("/zones/{zone_id}/applications", zone_id=zone_id),
body=await async_maybe_transform(
{
"identifier": identifier,
@@ -573,7 +573,7 @@ async def retrieve(
if not id:
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
return await self._get(
- f"/zones/{zone_id}/applications/{id}",
+ path_template("/zones/{zone_id}/applications/{id}", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -624,7 +624,7 @@ async def update(
if not id:
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
return await self._patch(
- f"/zones/{zone_id}/applications/{id}",
+ path_template("/zones/{zone_id}/applications/{id}", zone_id=zone_id, id=id),
body=await async_maybe_transform(
{
"description": description,
@@ -688,7 +688,7 @@ async def list(
if not zone_id:
raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
return await self._get(
- f"/zones/{zone_id}/applications",
+ path_template("/zones/{zone_id}/applications", zone_id=zone_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -742,7 +742,7 @@ async def delete(
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
return await self._delete(
- f"/zones/{zone_id}/applications/{id}",
+ path_template("/zones/{zone_id}/applications/{id}", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -789,7 +789,7 @@ async def list_credentials(
if not id:
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
return await self._get(
- f"/zones/{zone_id}/applications/{id}/application-credentials",
+ path_template("/zones/{zone_id}/applications/{id}/application-credentials", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -849,7 +849,7 @@ async def list_resources(
if not id:
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
return await self._get(
- f"/zones/{zone_id}/applications/{id}/resources",
+ path_template("/zones/{zone_id}/applications/{id}/resources", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
diff --git a/src/keycardai_api/resources/zones/applications/dependencies.py b/src/keycardai_api/resources/zones/applications/dependencies.py
index 626c895..2576e00 100644
--- a/src/keycardai_api/resources/zones/applications/dependencies.py
+++ b/src/keycardai_api/resources/zones/applications/dependencies.py
@@ -8,7 +8,7 @@
import httpx
from ...._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given
-from ...._utils import maybe_transform, async_maybe_transform
+from ...._utils import path_template, maybe_transform, async_maybe_transform
from ...._compat import cached_property
from ...._resource import SyncAPIResource, AsyncAPIResource
from ...._response import (
@@ -77,7 +77,12 @@ def retrieve(
if not dependency_id:
raise ValueError(f"Expected a non-empty value for `dependency_id` but received {dependency_id!r}")
return self._get(
- f"/zones/{zone_id}/applications/{id}/dependencies/{dependency_id}",
+ path_template(
+ "/zones/{zone_id}/applications/{id}/dependencies/{dependency_id}",
+ zone_id=zone_id,
+ id=id,
+ dependency_id=dependency_id,
+ ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -125,7 +130,7 @@ def list(
if not id:
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
return self._get(
- f"/zones/{zone_id}/applications/{id}/dependencies",
+ path_template("/zones/{zone_id}/applications/{id}/dependencies", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -180,7 +185,12 @@ def add(
raise ValueError(f"Expected a non-empty value for `dependency_id` but received {dependency_id!r}")
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
return self._put(
- f"/zones/{zone_id}/applications/{id}/dependencies/{dependency_id}",
+ path_template(
+ "/zones/{zone_id}/applications/{id}/dependencies/{dependency_id}",
+ zone_id=zone_id,
+ id=id,
+ dependency_id=dependency_id,
+ ),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -224,7 +234,12 @@ def remove(
raise ValueError(f"Expected a non-empty value for `dependency_id` but received {dependency_id!r}")
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
return self._delete(
- f"/zones/{zone_id}/applications/{id}/dependencies/{dependency_id}",
+ path_template(
+ "/zones/{zone_id}/applications/{id}/dependencies/{dependency_id}",
+ zone_id=zone_id,
+ id=id,
+ dependency_id=dependency_id,
+ ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -284,7 +299,12 @@ async def retrieve(
if not dependency_id:
raise ValueError(f"Expected a non-empty value for `dependency_id` but received {dependency_id!r}")
return await self._get(
- f"/zones/{zone_id}/applications/{id}/dependencies/{dependency_id}",
+ path_template(
+ "/zones/{zone_id}/applications/{id}/dependencies/{dependency_id}",
+ zone_id=zone_id,
+ id=id,
+ dependency_id=dependency_id,
+ ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -332,7 +352,7 @@ async def list(
if not id:
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
return await self._get(
- f"/zones/{zone_id}/applications/{id}/dependencies",
+ path_template("/zones/{zone_id}/applications/{id}/dependencies", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -387,7 +407,12 @@ async def add(
raise ValueError(f"Expected a non-empty value for `dependency_id` but received {dependency_id!r}")
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
return await self._put(
- f"/zones/{zone_id}/applications/{id}/dependencies/{dependency_id}",
+ path_template(
+ "/zones/{zone_id}/applications/{id}/dependencies/{dependency_id}",
+ zone_id=zone_id,
+ id=id,
+ dependency_id=dependency_id,
+ ),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -433,7 +458,12 @@ async def remove(
raise ValueError(f"Expected a non-empty value for `dependency_id` but received {dependency_id!r}")
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
return await self._delete(
- f"/zones/{zone_id}/applications/{id}/dependencies/{dependency_id}",
+ path_template(
+ "/zones/{zone_id}/applications/{id}/dependencies/{dependency_id}",
+ zone_id=zone_id,
+ id=id,
+ dependency_id=dependency_id,
+ ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
diff --git a/src/keycardai_api/resources/zones/delegated_grants.py b/src/keycardai_api/resources/zones/delegated_grants.py
index 60f5f17..9287fa8 100644
--- a/src/keycardai_api/resources/zones/delegated_grants.py
+++ b/src/keycardai_api/resources/zones/delegated_grants.py
@@ -8,7 +8,7 @@
import httpx
from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given
-from ..._utils import maybe_transform, async_maybe_transform
+from ..._utils import path_template, maybe_transform, async_maybe_transform
from ..._compat import cached_property
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import (
@@ -74,7 +74,7 @@ def retrieve(
if not id:
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
return self._get(
- f"/zones/{zone_id}/delegated-grants/{id}",
+ path_template("/zones/{zone_id}/delegated-grants/{id}", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -111,7 +111,7 @@ def update(
if not id:
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
return self._patch(
- f"/zones/{zone_id}/delegated-grants/{id}",
+ path_template("/zones/{zone_id}/delegated-grants/{id}", zone_id=zone_id, id=id),
body=maybe_transform({"status": status}, delegated_grant_update_params.DelegatedGrantUpdateParams),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
@@ -165,7 +165,7 @@ def list(
if not zone_id:
raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
return self._get(
- f"/zones/{zone_id}/delegated-grants",
+ path_template("/zones/{zone_id}/delegated-grants", zone_id=zone_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -219,7 +219,7 @@ def delete(
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
return self._delete(
- f"/zones/{zone_id}/delegated-grants/{id}",
+ path_template("/zones/{zone_id}/delegated-grants/{id}", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -276,7 +276,7 @@ async def retrieve(
if not id:
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
return await self._get(
- f"/zones/{zone_id}/delegated-grants/{id}",
+ path_template("/zones/{zone_id}/delegated-grants/{id}", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -313,7 +313,7 @@ async def update(
if not id:
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
return await self._patch(
- f"/zones/{zone_id}/delegated-grants/{id}",
+ path_template("/zones/{zone_id}/delegated-grants/{id}", zone_id=zone_id, id=id),
body=await async_maybe_transform(
{"status": status}, delegated_grant_update_params.DelegatedGrantUpdateParams
),
@@ -369,7 +369,7 @@ async def list(
if not zone_id:
raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
return await self._get(
- f"/zones/{zone_id}/delegated-grants",
+ path_template("/zones/{zone_id}/delegated-grants", zone_id=zone_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -423,7 +423,7 @@ async def delete(
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
return await self._delete(
- f"/zones/{zone_id}/delegated-grants/{id}",
+ path_template("/zones/{zone_id}/delegated-grants/{id}", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
diff --git a/src/keycardai_api/resources/zones/members.py b/src/keycardai_api/resources/zones/members.py
index e6298b5..2c0acb9 100644
--- a/src/keycardai_api/resources/zones/members.py
+++ b/src/keycardai_api/resources/zones/members.py
@@ -8,7 +8,7 @@
import httpx
from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given
-from ..._utils import maybe_transform, async_maybe_transform
+from ..._utils import path_template, maybe_transform, async_maybe_transform
from ..._compat import cached_property
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import (
@@ -77,7 +77,11 @@ def retrieve(
f"Expected a non-empty value for `organization_user_id` but received {organization_user_id!r}"
)
return self._get(
- f"/zones/{zone_id}/members/{organization_user_id}",
+ path_template(
+ "/zones/{zone_id}/members/{organization_user_id}",
+ zone_id=zone_id,
+ organization_user_id=organization_user_id,
+ ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -121,7 +125,11 @@ def update(
f"Expected a non-empty value for `organization_user_id` but received {organization_user_id!r}"
)
return self._patch(
- f"/zones/{zone_id}/members/{organization_user_id}",
+ path_template(
+ "/zones/{zone_id}/members/{organization_user_id}",
+ zone_id=zone_id,
+ organization_user_id=organization_user_id,
+ ),
body=maybe_transform({"role": role}, member_update_params.MemberUpdateParams),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
@@ -170,7 +178,7 @@ def list(
if not zone_id:
raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
return self._get(
- f"/zones/{zone_id}/members",
+ path_template("/zones/{zone_id}/members", zone_id=zone_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -224,7 +232,11 @@ def delete(
)
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
return self._delete(
- f"/zones/{zone_id}/members/{organization_user_id}",
+ path_template(
+ "/zones/{zone_id}/members/{organization_user_id}",
+ zone_id=zone_id,
+ organization_user_id=organization_user_id,
+ ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -264,7 +276,7 @@ def add(
if not zone_id:
raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
return self._post(
- f"/zones/{zone_id}/members",
+ path_template("/zones/{zone_id}/members", zone_id=zone_id),
body=maybe_transform(
{
"organization_user_id": organization_user_id,
@@ -330,7 +342,11 @@ async def retrieve(
f"Expected a non-empty value for `organization_user_id` but received {organization_user_id!r}"
)
return await self._get(
- f"/zones/{zone_id}/members/{organization_user_id}",
+ path_template(
+ "/zones/{zone_id}/members/{organization_user_id}",
+ zone_id=zone_id,
+ organization_user_id=organization_user_id,
+ ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -374,7 +390,11 @@ async def update(
f"Expected a non-empty value for `organization_user_id` but received {organization_user_id!r}"
)
return await self._patch(
- f"/zones/{zone_id}/members/{organization_user_id}",
+ path_template(
+ "/zones/{zone_id}/members/{organization_user_id}",
+ zone_id=zone_id,
+ organization_user_id=organization_user_id,
+ ),
body=await async_maybe_transform({"role": role}, member_update_params.MemberUpdateParams),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
@@ -423,7 +443,7 @@ async def list(
if not zone_id:
raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
return await self._get(
- f"/zones/{zone_id}/members",
+ path_template("/zones/{zone_id}/members", zone_id=zone_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -477,7 +497,11 @@ async def delete(
)
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
return await self._delete(
- f"/zones/{zone_id}/members/{organization_user_id}",
+ path_template(
+ "/zones/{zone_id}/members/{organization_user_id}",
+ zone_id=zone_id,
+ organization_user_id=organization_user_id,
+ ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -517,7 +541,7 @@ async def add(
if not zone_id:
raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
return await self._post(
- f"/zones/{zone_id}/members",
+ path_template("/zones/{zone_id}/members", zone_id=zone_id),
body=await async_maybe_transform(
{
"organization_user_id": organization_user_id,
diff --git a/src/keycardai_api/resources/zones/policies/policies.py b/src/keycardai_api/resources/zones/policies/policies.py
index b697838..5b19a97 100644
--- a/src/keycardai_api/resources/zones/policies/policies.py
+++ b/src/keycardai_api/resources/zones/policies/policies.py
@@ -16,7 +16,7 @@
AsyncVersionsResourceWithStreamingResponse,
)
from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
-from ...._utils import maybe_transform, strip_not_given, async_maybe_transform
+from ...._utils import path_template, maybe_transform, strip_not_given, async_maybe_transform
from ...._compat import cached_property
from ...._resource import SyncAPIResource, AsyncAPIResource
from ...._response import (
@@ -99,7 +99,7 @@ def create(
**(extra_headers or {}),
}
return self._post(
- f"/zones/{zone_id}/policies",
+ path_template("/zones/{zone_id}/policies", zone_id=zone_id),
body=maybe_transform(
{
"name": name,
@@ -153,7 +153,7 @@ def retrieve(
**(extra_headers or {}),
}
return self._get(
- f"/zones/{zone_id}/policies/{policy_id}",
+ path_template("/zones/{zone_id}/policies/{policy_id}", zone_id=zone_id, policy_id=policy_id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -204,7 +204,7 @@ def update(
**(extra_headers or {}),
}
return self._patch(
- f"/zones/{zone_id}/policies/{policy_id}",
+ path_template("/zones/{zone_id}/policies/{policy_id}", zone_id=zone_id, policy_id=policy_id),
body=maybe_transform(
{
"description": description,
@@ -275,7 +275,7 @@ def list(
**(extra_headers or {}),
}
return self._get(
- f"/zones/{zone_id}/policies",
+ path_template("/zones/{zone_id}/policies", zone_id=zone_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -336,7 +336,7 @@ def archive(
**(extra_headers or {}),
}
return self._delete(
- f"/zones/{zone_id}/policies/{policy_id}",
+ path_template("/zones/{zone_id}/policies/{policy_id}", zone_id=zone_id, policy_id=policy_id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -410,7 +410,7 @@ async def create(
**(extra_headers or {}),
}
return await self._post(
- f"/zones/{zone_id}/policies",
+ path_template("/zones/{zone_id}/policies", zone_id=zone_id),
body=await async_maybe_transform(
{
"name": name,
@@ -464,7 +464,7 @@ async def retrieve(
**(extra_headers or {}),
}
return await self._get(
- f"/zones/{zone_id}/policies/{policy_id}",
+ path_template("/zones/{zone_id}/policies/{policy_id}", zone_id=zone_id, policy_id=policy_id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -515,7 +515,7 @@ async def update(
**(extra_headers or {}),
}
return await self._patch(
- f"/zones/{zone_id}/policies/{policy_id}",
+ path_template("/zones/{zone_id}/policies/{policy_id}", zone_id=zone_id, policy_id=policy_id),
body=await async_maybe_transform(
{
"description": description,
@@ -586,7 +586,7 @@ async def list(
**(extra_headers or {}),
}
return await self._get(
- f"/zones/{zone_id}/policies",
+ path_template("/zones/{zone_id}/policies", zone_id=zone_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -647,7 +647,7 @@ async def archive(
**(extra_headers or {}),
}
return await self._delete(
- f"/zones/{zone_id}/policies/{policy_id}",
+ path_template("/zones/{zone_id}/policies/{policy_id}", zone_id=zone_id, policy_id=policy_id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
diff --git a/src/keycardai_api/resources/zones/policies/versions.py b/src/keycardai_api/resources/zones/policies/versions.py
index f343374..1caa111 100644
--- a/src/keycardai_api/resources/zones/policies/versions.py
+++ b/src/keycardai_api/resources/zones/policies/versions.py
@@ -8,7 +8,7 @@
import httpx
from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
-from ...._utils import maybe_transform, strip_not_given, async_maybe_transform
+from ...._utils import path_template, maybe_transform, strip_not_given, async_maybe_transform
from ...._compat import cached_property
from ...._resource import SyncAPIResource, AsyncAPIResource
from ...._response import (
@@ -68,6 +68,8 @@ def create(
Create a new immutable policy version
Args:
+ schema_version: Schema version to validate this policy against. Must not be archived.
+
cedar_json: Cedar policy in JSON representation. Mutually exclusive with cedar_raw.
cedar_raw: Cedar policy in human-readable Cedar syntax. Mutually exclusive with cedar_json.
@@ -94,7 +96,7 @@ def create(
**(extra_headers or {}),
}
return self._post(
- f"/zones/{zone_id}/policies/{policy_id}/versions",
+ path_template("/zones/{zone_id}/policies/{policy_id}/versions", zone_id=zone_id, policy_id=policy_id),
body=maybe_transform(
{
"schema_version": schema_version,
@@ -157,7 +159,12 @@ def retrieve(
**(extra_headers or {}),
}
return self._get(
- f"/zones/{zone_id}/policies/{policy_id}/versions/{version_id}",
+ path_template(
+ "/zones/{zone_id}/policies/{policy_id}/versions/{version_id}",
+ zone_id=zone_id,
+ policy_id=policy_id,
+ version_id=version_id,
+ ),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -232,7 +239,7 @@ def list(
**(extra_headers or {}),
}
return self._get(
- f"/zones/{zone_id}/policies/{policy_id}/versions",
+ path_template("/zones/{zone_id}/policies/{policy_id}/versions", zone_id=zone_id, policy_id=policy_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -297,7 +304,12 @@ def archive(
**(extra_headers or {}),
}
return self._delete(
- f"/zones/{zone_id}/policies/{policy_id}/versions/{version_id}",
+ path_template(
+ "/zones/{zone_id}/policies/{policy_id}/versions/{version_id}",
+ zone_id=zone_id,
+ policy_id=policy_id,
+ version_id=version_id,
+ ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -348,6 +360,8 @@ async def create(
Create a new immutable policy version
Args:
+ schema_version: Schema version to validate this policy against. Must not be archived.
+
cedar_json: Cedar policy in JSON representation. Mutually exclusive with cedar_raw.
cedar_raw: Cedar policy in human-readable Cedar syntax. Mutually exclusive with cedar_json.
@@ -374,7 +388,7 @@ async def create(
**(extra_headers or {}),
}
return await self._post(
- f"/zones/{zone_id}/policies/{policy_id}/versions",
+ path_template("/zones/{zone_id}/policies/{policy_id}/versions", zone_id=zone_id, policy_id=policy_id),
body=await async_maybe_transform(
{
"schema_version": schema_version,
@@ -437,7 +451,12 @@ async def retrieve(
**(extra_headers or {}),
}
return await self._get(
- f"/zones/{zone_id}/policies/{policy_id}/versions/{version_id}",
+ path_template(
+ "/zones/{zone_id}/policies/{policy_id}/versions/{version_id}",
+ zone_id=zone_id,
+ policy_id=policy_id,
+ version_id=version_id,
+ ),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -512,7 +531,7 @@ async def list(
**(extra_headers or {}),
}
return await self._get(
- f"/zones/{zone_id}/policies/{policy_id}/versions",
+ path_template("/zones/{zone_id}/policies/{policy_id}/versions", zone_id=zone_id, policy_id=policy_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -577,7 +596,12 @@ async def archive(
**(extra_headers or {}),
}
return await self._delete(
- f"/zones/{zone_id}/policies/{policy_id}/versions/{version_id}",
+ path_template(
+ "/zones/{zone_id}/policies/{policy_id}/versions/{version_id}",
+ zone_id=zone_id,
+ policy_id=policy_id,
+ version_id=version_id,
+ ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
diff --git a/src/keycardai_api/resources/zones/policy_schemas.py b/src/keycardai_api/resources/zones/policy_schemas.py
index f04688f..6d1d161 100644
--- a/src/keycardai_api/resources/zones/policy_schemas.py
+++ b/src/keycardai_api/resources/zones/policy_schemas.py
@@ -8,7 +8,7 @@
import httpx
from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
-from ..._utils import maybe_transform, strip_not_given, async_maybe_transform
+from ..._utils import path_template, maybe_transform, strip_not_given, async_maybe_transform
from ..._compat import cached_property
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import (
@@ -26,7 +26,22 @@
class PolicySchemasResource(SyncAPIResource):
- """Zone-scoped Cedar schema management"""
+ """Zone-scoped Cedar schema management.
+
+ The Cedar schema defines the entity model used for authorization decisions.
+ Key entity types and their attributes:
+
+ - **Keycard::User** — `email` (String), `groups` (Set of String)
+ - **Keycard::Application** — `registration_method` (RegistrationMethod entity), `credential_type` (CredentialType entity)
+ - **Keycard::RegistrationMethod** — enum entity: `"managed"`, `"dcr"`
+ - **Keycard::CredentialType** — enum entity: `"token"`, `"password"`, `"public-key"`, `"url"`, `"public"`
+ - **Keycard::Resource** — `id` (String), `name` (String), `scopes` (Set of String)
+ - **Keycard::Claims** — `email` (String), `groups` (Set of String), plus arbitrary additional fields
+
+ Enum-like attributes use Cedar enum entity types (schema version `2026-03-16`+).
+ In policies, reference values as `RegistrationMethod::"managed"` or `CredentialType::"token"`.
+ See the Credentials API spec for the full entity model reference.
+ """
@cached_property
def with_raw_response(self) -> PolicySchemasResourceWithRawResponse:
@@ -92,7 +107,7 @@ def retrieve(
**(extra_headers or {}),
}
return self._get(
- f"/zones/{zone_id}/policy-schemas/{version}",
+ path_template("/zones/{zone_id}/policy-schemas/{version}", zone_id=zone_id, version=version),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -169,7 +184,7 @@ def list(
**(extra_headers or {}),
}
return self._get(
- f"/zones/{zone_id}/policy-schemas",
+ path_template("/zones/{zone_id}/policy-schemas", zone_id=zone_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -233,7 +248,7 @@ def set_default(
**(extra_headers or {}),
}
return self._patch(
- f"/zones/{zone_id}/policy-schemas/{version}",
+ path_template("/zones/{zone_id}/policy-schemas/{version}", zone_id=zone_id, version=version),
body=maybe_transform(body, policy_schema_set_default_params.PolicySchemaSetDefaultParams),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
@@ -243,7 +258,22 @@ def set_default(
class AsyncPolicySchemasResource(AsyncAPIResource):
- """Zone-scoped Cedar schema management"""
+ """Zone-scoped Cedar schema management.
+
+ The Cedar schema defines the entity model used for authorization decisions.
+ Key entity types and their attributes:
+
+ - **Keycard::User** — `email` (String), `groups` (Set of String)
+ - **Keycard::Application** — `registration_method` (RegistrationMethod entity), `credential_type` (CredentialType entity)
+ - **Keycard::RegistrationMethod** — enum entity: `"managed"`, `"dcr"`
+ - **Keycard::CredentialType** — enum entity: `"token"`, `"password"`, `"public-key"`, `"url"`, `"public"`
+ - **Keycard::Resource** — `id` (String), `name` (String), `scopes` (Set of String)
+ - **Keycard::Claims** — `email` (String), `groups` (Set of String), plus arbitrary additional fields
+
+ Enum-like attributes use Cedar enum entity types (schema version `2026-03-16`+).
+ In policies, reference values as `RegistrationMethod::"managed"` or `CredentialType::"token"`.
+ See the Credentials API spec for the full entity model reference.
+ """
@cached_property
def with_raw_response(self) -> AsyncPolicySchemasResourceWithRawResponse:
@@ -309,7 +339,7 @@ async def retrieve(
**(extra_headers or {}),
}
return await self._get(
- f"/zones/{zone_id}/policy-schemas/{version}",
+ path_template("/zones/{zone_id}/policy-schemas/{version}", zone_id=zone_id, version=version),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -388,7 +418,7 @@ async def list(
**(extra_headers or {}),
}
return await self._get(
- f"/zones/{zone_id}/policy-schemas",
+ path_template("/zones/{zone_id}/policy-schemas", zone_id=zone_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -452,7 +482,7 @@ async def set_default(
**(extra_headers or {}),
}
return await self._patch(
- f"/zones/{zone_id}/policy-schemas/{version}",
+ path_template("/zones/{zone_id}/policy-schemas/{version}", zone_id=zone_id, version=version),
body=await async_maybe_transform(body, policy_schema_set_default_params.PolicySchemaSetDefaultParams),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
diff --git a/src/keycardai_api/resources/zones/policy_sets/policy_sets.py b/src/keycardai_api/resources/zones/policy_sets/policy_sets.py
index af4531a..0445925 100644
--- a/src/keycardai_api/resources/zones/policy_sets/policy_sets.py
+++ b/src/keycardai_api/resources/zones/policy_sets/policy_sets.py
@@ -16,7 +16,7 @@
AsyncVersionsResourceWithStreamingResponse,
)
from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
-from ...._utils import maybe_transform, strip_not_given, async_maybe_transform
+from ...._utils import path_template, maybe_transform, strip_not_given, async_maybe_transform
from ...._compat import cached_property
from ...._resource import SyncAPIResource, AsyncAPIResource
from ...._response import (
@@ -81,6 +81,14 @@ def create(
version.
Args:
+ scope_type:
+ The scope at which this policy set applies:
+
+ - `"zone"` — applies to all requests in the zone.
+ - `"resource"` — scoped to a specific resource.
+ - `"user"` — scoped to a specific user.
+ - `"session"` — scoped to a specific session.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -101,7 +109,7 @@ def create(
**(extra_headers or {}),
}
return self._post(
- f"/zones/{zone_id}/policy-sets",
+ path_template("/zones/{zone_id}/policy-sets", zone_id=zone_id),
body=maybe_transform(
{
"name": name,
@@ -155,7 +163,7 @@ def retrieve(
**(extra_headers or {}),
}
return self._get(
- f"/zones/{zone_id}/policy-sets/{policy_set_id}",
+ path_template("/zones/{zone_id}/policy-sets/{policy_set_id}", zone_id=zone_id, policy_set_id=policy_set_id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -207,7 +215,7 @@ def update(
**(extra_headers or {}),
}
return self._patch(
- f"/zones/{zone_id}/policy-sets/{policy_set_id}",
+ path_template("/zones/{zone_id}/policy-sets/{policy_set_id}", zone_id=zone_id, policy_set_id=policy_set_id),
body=maybe_transform({"name": name}, policy_set_update_params.PolicySetUpdateParams),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
@@ -272,7 +280,7 @@ def list(
**(extra_headers or {}),
}
return self._get(
- f"/zones/{zone_id}/policy-sets",
+ path_template("/zones/{zone_id}/policy-sets", zone_id=zone_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -335,7 +343,7 @@ def archive(
**(extra_headers or {}),
}
return self._delete(
- f"/zones/{zone_id}/policy-sets/{policy_set_id}",
+ path_template("/zones/{zone_id}/policy-sets/{policy_set_id}", zone_id=zone_id, policy_set_id=policy_set_id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -391,6 +399,14 @@ async def create(
version.
Args:
+ scope_type:
+ The scope at which this policy set applies:
+
+ - `"zone"` — applies to all requests in the zone.
+ - `"resource"` — scoped to a specific resource.
+ - `"user"` — scoped to a specific user.
+ - `"session"` — scoped to a specific session.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -411,7 +427,7 @@ async def create(
**(extra_headers or {}),
}
return await self._post(
- f"/zones/{zone_id}/policy-sets",
+ path_template("/zones/{zone_id}/policy-sets", zone_id=zone_id),
body=await async_maybe_transform(
{
"name": name,
@@ -465,7 +481,7 @@ async def retrieve(
**(extra_headers or {}),
}
return await self._get(
- f"/zones/{zone_id}/policy-sets/{policy_set_id}",
+ path_template("/zones/{zone_id}/policy-sets/{policy_set_id}", zone_id=zone_id, policy_set_id=policy_set_id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -517,7 +533,7 @@ async def update(
**(extra_headers or {}),
}
return await self._patch(
- f"/zones/{zone_id}/policy-sets/{policy_set_id}",
+ path_template("/zones/{zone_id}/policy-sets/{policy_set_id}", zone_id=zone_id, policy_set_id=policy_set_id),
body=await async_maybe_transform({"name": name}, policy_set_update_params.PolicySetUpdateParams),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
@@ -582,7 +598,7 @@ async def list(
**(extra_headers or {}),
}
return await self._get(
- f"/zones/{zone_id}/policy-sets",
+ path_template("/zones/{zone_id}/policy-sets", zone_id=zone_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -645,7 +661,7 @@ async def archive(
**(extra_headers or {}),
}
return await self._delete(
- f"/zones/{zone_id}/policy-sets/{policy_set_id}",
+ path_template("/zones/{zone_id}/policy-sets/{policy_set_id}", zone_id=zone_id, policy_set_id=policy_set_id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
diff --git a/src/keycardai_api/resources/zones/policy_sets/versions.py b/src/keycardai_api/resources/zones/policy_sets/versions.py
index 8007874..0d8a521 100644
--- a/src/keycardai_api/resources/zones/policy_sets/versions.py
+++ b/src/keycardai_api/resources/zones/policy_sets/versions.py
@@ -8,7 +8,7 @@
import httpx
from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
-from ...._utils import maybe_transform, strip_not_given, async_maybe_transform
+from ...._utils import path_template, maybe_transform, strip_not_given, async_maybe_transform
from ...._compat import cached_property
from ...._resource import SyncAPIResource, AsyncAPIResource
from ...._response import (
@@ -74,6 +74,8 @@ def create(
Validates the manifest, computes SHA, and creates an immutable version snapshot.
Args:
+ schema_version: Schema version to pin to this policy set version.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -96,7 +98,9 @@ def create(
**(extra_headers or {}),
}
return self._post(
- f"/zones/{zone_id}/policy-sets/{policy_set_id}/versions",
+ path_template(
+ "/zones/{zone_id}/policy-sets/{policy_set_id}/versions", zone_id=zone_id, policy_set_id=policy_set_id
+ ),
body=maybe_transform(
{
"manifest": manifest,
@@ -153,7 +157,12 @@ def retrieve(
**(extra_headers or {}),
}
return self._get(
- f"/zones/{zone_id}/policy-sets/{policy_set_id}/versions/{version_id}",
+ path_template(
+ "/zones/{zone_id}/policy-sets/{policy_set_id}/versions/{version_id}",
+ zone_id=zone_id,
+ policy_set_id=policy_set_id,
+ version_id=version_id,
+ ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -206,7 +215,12 @@ def update(
**(extra_headers or {}),
}
return self._patch(
- f"/zones/{zone_id}/policy-sets/{policy_set_id}/versions/{version_id}",
+ path_template(
+ "/zones/{zone_id}/policy-sets/{policy_set_id}/versions/{version_id}",
+ zone_id=zone_id,
+ policy_set_id=policy_set_id,
+ version_id=version_id,
+ ),
body=maybe_transform({"active": active}, version_update_params.VersionUpdateParams),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
@@ -274,7 +288,9 @@ def list(
**(extra_headers or {}),
}
return self._get(
- f"/zones/{zone_id}/policy-sets/{policy_set_id}/versions",
+ path_template(
+ "/zones/{zone_id}/policy-sets/{policy_set_id}/versions", zone_id=zone_id, policy_set_id=policy_set_id
+ ),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -338,7 +354,12 @@ def archive(
**(extra_headers or {}),
}
return self._delete(
- f"/zones/{zone_id}/policy-sets/{policy_set_id}/versions/{version_id}",
+ path_template(
+ "/zones/{zone_id}/policy-sets/{policy_set_id}/versions/{version_id}",
+ zone_id=zone_id,
+ policy_set_id=policy_set_id,
+ version_id=version_id,
+ ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -413,7 +434,12 @@ def list_policies(
**(extra_headers or {}),
}
return self._get(
- f"/zones/{zone_id}/policy-sets/{policy_set_id}/versions/{version_id}/policies",
+ path_template(
+ "/zones/{zone_id}/policy-sets/{policy_set_id}/versions/{version_id}/policies",
+ zone_id=zone_id,
+ policy_set_id=policy_set_id,
+ version_id=version_id,
+ ),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -478,6 +504,8 @@ async def create(
Validates the manifest, computes SHA, and creates an immutable version snapshot.
Args:
+ schema_version: Schema version to pin to this policy set version.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -500,7 +528,9 @@ async def create(
**(extra_headers or {}),
}
return await self._post(
- f"/zones/{zone_id}/policy-sets/{policy_set_id}/versions",
+ path_template(
+ "/zones/{zone_id}/policy-sets/{policy_set_id}/versions", zone_id=zone_id, policy_set_id=policy_set_id
+ ),
body=await async_maybe_transform(
{
"manifest": manifest,
@@ -557,7 +587,12 @@ async def retrieve(
**(extra_headers or {}),
}
return await self._get(
- f"/zones/{zone_id}/policy-sets/{policy_set_id}/versions/{version_id}",
+ path_template(
+ "/zones/{zone_id}/policy-sets/{policy_set_id}/versions/{version_id}",
+ zone_id=zone_id,
+ policy_set_id=policy_set_id,
+ version_id=version_id,
+ ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -610,7 +645,12 @@ async def update(
**(extra_headers or {}),
}
return await self._patch(
- f"/zones/{zone_id}/policy-sets/{policy_set_id}/versions/{version_id}",
+ path_template(
+ "/zones/{zone_id}/policy-sets/{policy_set_id}/versions/{version_id}",
+ zone_id=zone_id,
+ policy_set_id=policy_set_id,
+ version_id=version_id,
+ ),
body=await async_maybe_transform({"active": active}, version_update_params.VersionUpdateParams),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
@@ -678,7 +718,9 @@ async def list(
**(extra_headers or {}),
}
return await self._get(
- f"/zones/{zone_id}/policy-sets/{policy_set_id}/versions",
+ path_template(
+ "/zones/{zone_id}/policy-sets/{policy_set_id}/versions", zone_id=zone_id, policy_set_id=policy_set_id
+ ),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -742,7 +784,12 @@ async def archive(
**(extra_headers or {}),
}
return await self._delete(
- f"/zones/{zone_id}/policy-sets/{policy_set_id}/versions/{version_id}",
+ path_template(
+ "/zones/{zone_id}/policy-sets/{policy_set_id}/versions/{version_id}",
+ zone_id=zone_id,
+ policy_set_id=policy_set_id,
+ version_id=version_id,
+ ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -817,7 +864,12 @@ async def list_policies(
**(extra_headers or {}),
}
return await self._get(
- f"/zones/{zone_id}/policy-sets/{policy_set_id}/versions/{version_id}/policies",
+ path_template(
+ "/zones/{zone_id}/policy-sets/{policy_set_id}/versions/{version_id}/policies",
+ zone_id=zone_id,
+ policy_set_id=policy_set_id,
+ version_id=version_id,
+ ),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
diff --git a/src/keycardai_api/resources/zones/providers.py b/src/keycardai_api/resources/zones/providers.py
index 8b22181..91273c4 100644
--- a/src/keycardai_api/resources/zones/providers.py
+++ b/src/keycardai_api/resources/zones/providers.py
@@ -8,7 +8,7 @@
import httpx
from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given
-from ..._utils import maybe_transform, async_maybe_transform
+from ..._utils import path_template, maybe_transform, async_maybe_transform
from ..._compat import cached_property
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import (
@@ -93,7 +93,7 @@ def create(
if not zone_id:
raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
return self._post(
- f"/zones/{zone_id}/providers",
+ path_template("/zones/{zone_id}/providers", zone_id=zone_id),
body=maybe_transform(
{
"identifier": identifier,
@@ -141,7 +141,7 @@ def retrieve(
if not id:
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
return self._get(
- f"/zones/{zone_id}/providers/{id}",
+ path_template("/zones/{zone_id}/providers/{id}", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -199,7 +199,7 @@ def update(
if not id:
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
return self._patch(
- f"/zones/{zone_id}/providers/{id}",
+ path_template("/zones/{zone_id}/providers/{id}", zone_id=zone_id, id=id),
body=maybe_transform(
{
"client_id": client_id,
@@ -258,7 +258,7 @@ def list(
if not zone_id:
raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
return self._get(
- f"/zones/{zone_id}/providers",
+ path_template("/zones/{zone_id}/providers", zone_id=zone_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -311,7 +311,7 @@ def delete(
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
return self._delete(
- f"/zones/{zone_id}/providers/{id}",
+ path_template("/zones/{zone_id}/providers/{id}", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -387,7 +387,7 @@ async def create(
if not zone_id:
raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
return await self._post(
- f"/zones/{zone_id}/providers",
+ path_template("/zones/{zone_id}/providers", zone_id=zone_id),
body=await async_maybe_transform(
{
"identifier": identifier,
@@ -435,7 +435,7 @@ async def retrieve(
if not id:
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
return await self._get(
- f"/zones/{zone_id}/providers/{id}",
+ path_template("/zones/{zone_id}/providers/{id}", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -493,7 +493,7 @@ async def update(
if not id:
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
return await self._patch(
- f"/zones/{zone_id}/providers/{id}",
+ path_template("/zones/{zone_id}/providers/{id}", zone_id=zone_id, id=id),
body=await async_maybe_transform(
{
"client_id": client_id,
@@ -552,7 +552,7 @@ async def list(
if not zone_id:
raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
return await self._get(
- f"/zones/{zone_id}/providers",
+ path_template("/zones/{zone_id}/providers", zone_id=zone_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -605,7 +605,7 @@ async def delete(
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
return await self._delete(
- f"/zones/{zone_id}/providers/{id}",
+ path_template("/zones/{zone_id}/providers/{id}", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
diff --git a/src/keycardai_api/resources/zones/resources.py b/src/keycardai_api/resources/zones/resources.py
index 5bc6e73..88404c1 100644
--- a/src/keycardai_api/resources/zones/resources.py
+++ b/src/keycardai_api/resources/zones/resources.py
@@ -8,7 +8,7 @@
import httpx
from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given
-from ..._utils import maybe_transform, async_maybe_transform
+from ..._utils import path_template, maybe_transform, async_maybe_transform
from ..._compat import cached_property
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import (
@@ -104,7 +104,7 @@ def create(
if not zone_id:
raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
return self._post(
- f"/zones/{zone_id}/resources",
+ path_template("/zones/{zone_id}/resources", zone_id=zone_id),
body=maybe_transform(
{
"identifier": identifier,
@@ -153,7 +153,7 @@ def retrieve(
if not id:
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
return self._get(
- f"/zones/{zone_id}/resources/{id}",
+ path_template("/zones/{zone_id}/resources/{id}", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -216,7 +216,7 @@ def update(
if not id:
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
return self._patch(
- f"/zones/{zone_id}/resources/{id}",
+ path_template("/zones/{zone_id}/resources/{id}", zone_id=zone_id, id=id),
body=maybe_transform(
{
"application_id": application_id,
@@ -279,7 +279,7 @@ def list(
if not zone_id:
raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
return self._get(
- f"/zones/{zone_id}/resources",
+ path_template("/zones/{zone_id}/resources", zone_id=zone_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -331,7 +331,7 @@ def delete(
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
return self._delete(
- f"/zones/{zone_id}/resources/{id}",
+ path_template("/zones/{zone_id}/resources/{id}", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -412,7 +412,7 @@ async def create(
if not zone_id:
raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
return await self._post(
- f"/zones/{zone_id}/resources",
+ path_template("/zones/{zone_id}/resources", zone_id=zone_id),
body=await async_maybe_transform(
{
"identifier": identifier,
@@ -461,7 +461,7 @@ async def retrieve(
if not id:
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
return await self._get(
- f"/zones/{zone_id}/resources/{id}",
+ path_template("/zones/{zone_id}/resources/{id}", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -524,7 +524,7 @@ async def update(
if not id:
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
return await self._patch(
- f"/zones/{zone_id}/resources/{id}",
+ path_template("/zones/{zone_id}/resources/{id}", zone_id=zone_id, id=id),
body=await async_maybe_transform(
{
"application_id": application_id,
@@ -587,7 +587,7 @@ async def list(
if not zone_id:
raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
return await self._get(
- f"/zones/{zone_id}/resources",
+ path_template("/zones/{zone_id}/resources", zone_id=zone_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -639,7 +639,7 @@ async def delete(
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
return await self._delete(
- f"/zones/{zone_id}/resources/{id}",
+ path_template("/zones/{zone_id}/resources/{id}", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
diff --git a/src/keycardai_api/resources/zones/secrets.py b/src/keycardai_api/resources/zones/secrets.py
index 96218e0..bce28cd 100644
--- a/src/keycardai_api/resources/zones/secrets.py
+++ b/src/keycardai_api/resources/zones/secrets.py
@@ -7,7 +7,7 @@
import httpx
from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given
-from ..._utils import maybe_transform, strip_not_given, async_maybe_transform
+from ..._utils import path_template, maybe_transform, strip_not_given, async_maybe_transform
from ..._compat import cached_property
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import (
@@ -91,7 +91,7 @@ def create(
raise ValueError(f"Expected a non-empty value for `path_zone_id` but received {path_zone_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return self._post(
- f"/zones/{path_zone_id}/secrets",
+ path_template("/zones/{path_zone_id}/secrets", path_zone_id=path_zone_id),
body=maybe_transform(
{
"data": data,
@@ -140,7 +140,7 @@ def retrieve(
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return self._get(
- f"/zones/{zone_id}/secrets/{id}",
+ path_template("/zones/{zone_id}/secrets/{id}", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -188,7 +188,7 @@ def update(
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return self._patch(
- f"/zones/{zone_id}/secrets/{id}",
+ path_template("/zones/{zone_id}/secrets/{id}", zone_id=zone_id, id=id),
body=maybe_transform(
{
"data": data,
@@ -238,7 +238,7 @@ def list(
raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return self._get(
- f"/zones/{zone_id}/secrets",
+ path_template("/zones/{zone_id}/secrets", zone_id=zone_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -287,7 +287,7 @@ def delete(
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return self._delete(
- f"/zones/{zone_id}/secrets/{id}",
+ path_template("/zones/{zone_id}/secrets/{id}", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -361,7 +361,7 @@ async def create(
raise ValueError(f"Expected a non-empty value for `path_zone_id` but received {path_zone_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return await self._post(
- f"/zones/{path_zone_id}/secrets",
+ path_template("/zones/{path_zone_id}/secrets", path_zone_id=path_zone_id),
body=await async_maybe_transform(
{
"data": data,
@@ -410,7 +410,7 @@ async def retrieve(
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return await self._get(
- f"/zones/{zone_id}/secrets/{id}",
+ path_template("/zones/{zone_id}/secrets/{id}", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -458,7 +458,7 @@ async def update(
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return await self._patch(
- f"/zones/{zone_id}/secrets/{id}",
+ path_template("/zones/{zone_id}/secrets/{id}", zone_id=zone_id, id=id),
body=await async_maybe_transform(
{
"data": data,
@@ -508,7 +508,7 @@ async def list(
raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return await self._get(
- f"/zones/{zone_id}/secrets",
+ path_template("/zones/{zone_id}/secrets", zone_id=zone_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -557,7 +557,7 @@ async def delete(
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
extra_headers = {**strip_not_given({"X-Client-Request-ID": x_client_request_id}), **(extra_headers or {})}
return await self._delete(
- f"/zones/{zone_id}/secrets/{id}",
+ path_template("/zones/{zone_id}/secrets/{id}", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
diff --git a/src/keycardai_api/resources/zones/sessions.py b/src/keycardai_api/resources/zones/sessions.py
index 1e1b4bf..3a929ca 100644
--- a/src/keycardai_api/resources/zones/sessions.py
+++ b/src/keycardai_api/resources/zones/sessions.py
@@ -8,7 +8,7 @@
import httpx
from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given
-from ..._utils import maybe_transform, async_maybe_transform
+from ..._utils import path_template, maybe_transform, async_maybe_transform
from ..._compat import cached_property
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import (
@@ -76,7 +76,7 @@ def retrieve(
return cast(
Session,
self._get(
- f"/zones/{zone_id}/sessions/{id}",
+ path_template("/zones/{zone_id}/sessions/{id}", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -116,7 +116,7 @@ def update(
return cast(
Session,
self._patch(
- f"/zones/{zone_id}/sessions/{id}",
+ path_template("/zones/{zone_id}/sessions/{id}", zone_id=zone_id, id=id),
body=maybe_transform({"status": status}, session_update_params.SessionUpdateParams),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
@@ -176,7 +176,7 @@ def list(
if not zone_id:
raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
return self._get(
- f"/zones/{zone_id}/sessions",
+ path_template("/zones/{zone_id}/sessions", zone_id=zone_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -230,7 +230,7 @@ def delete(
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
return self._delete(
- f"/zones/{zone_id}/sessions/{id}",
+ path_template("/zones/{zone_id}/sessions/{id}", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -289,7 +289,7 @@ async def retrieve(
return cast(
Session,
await self._get(
- f"/zones/{zone_id}/sessions/{id}",
+ path_template("/zones/{zone_id}/sessions/{id}", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -329,7 +329,7 @@ async def update(
return cast(
Session,
await self._patch(
- f"/zones/{zone_id}/sessions/{id}",
+ path_template("/zones/{zone_id}/sessions/{id}", zone_id=zone_id, id=id),
body=await async_maybe_transform({"status": status}, session_update_params.SessionUpdateParams),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
@@ -389,7 +389,7 @@ async def list(
if not zone_id:
raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
return await self._get(
- f"/zones/{zone_id}/sessions",
+ path_template("/zones/{zone_id}/sessions", zone_id=zone_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -443,7 +443,7 @@ async def delete(
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
return await self._delete(
- f"/zones/{zone_id}/sessions/{id}",
+ path_template("/zones/{zone_id}/sessions/{id}", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
diff --git a/src/keycardai_api/resources/zones/user_agents.py b/src/keycardai_api/resources/zones/user_agents.py
index 79c64de..9f43cc1 100644
--- a/src/keycardai_api/resources/zones/user_agents.py
+++ b/src/keycardai_api/resources/zones/user_agents.py
@@ -8,7 +8,7 @@
import httpx
from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
-from ..._utils import maybe_transform, async_maybe_transform
+from ..._utils import path_template, maybe_transform, async_maybe_transform
from ..._compat import cached_property
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import (
@@ -74,7 +74,7 @@ def retrieve(
if not id:
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
return self._get(
- f"/zones/{zone_id}/user-agents/{id}",
+ path_template("/zones/{zone_id}/user-agents/{id}", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -120,7 +120,7 @@ def list(
if not zone_id:
raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
return self._get(
- f"/zones/{zone_id}/user-agents",
+ path_template("/zones/{zone_id}/user-agents", zone_id=zone_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -189,7 +189,7 @@ async def retrieve(
if not id:
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
return await self._get(
- f"/zones/{zone_id}/user-agents/{id}",
+ path_template("/zones/{zone_id}/user-agents/{id}", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -235,7 +235,7 @@ async def list(
if not zone_id:
raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
return await self._get(
- f"/zones/{zone_id}/user-agents",
+ path_template("/zones/{zone_id}/user-agents", zone_id=zone_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
diff --git a/src/keycardai_api/resources/zones/users.py b/src/keycardai_api/resources/zones/users.py
index 1d4022e..4edd9f4 100644
--- a/src/keycardai_api/resources/zones/users.py
+++ b/src/keycardai_api/resources/zones/users.py
@@ -8,7 +8,7 @@
import httpx
from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
-from ..._utils import maybe_transform, async_maybe_transform
+from ..._utils import path_template, maybe_transform, async_maybe_transform
from ..._compat import cached_property
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import (
@@ -74,7 +74,7 @@ def retrieve(
if not id:
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
return self._get(
- f"/zones/{zone_id}/users/{id}",
+ path_template("/zones/{zone_id}/users/{id}", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -118,7 +118,7 @@ def list(
if not zone_id:
raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
return self._get(
- f"/zones/{zone_id}/users",
+ path_template("/zones/{zone_id}/users", zone_id=zone_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -187,7 +187,7 @@ async def retrieve(
if not id:
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
return await self._get(
- f"/zones/{zone_id}/users/{id}",
+ path_template("/zones/{zone_id}/users/{id}", zone_id=zone_id, id=id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -231,7 +231,7 @@ async def list(
if not zone_id:
raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
return await self._get(
- f"/zones/{zone_id}/users",
+ path_template("/zones/{zone_id}/users", zone_id=zone_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
diff --git a/src/keycardai_api/resources/zones/zones.py b/src/keycardai_api/resources/zones/zones.py
index 02a12ff..06cf968 100644
--- a/src/keycardai_api/resources/zones/zones.py
+++ b/src/keycardai_api/resources/zones/zones.py
@@ -20,7 +20,6 @@
zone_create_params,
zone_update_params,
zone_retrieve_params,
- zone_list_session_resource_access_params,
)
from .members import (
MembersResource,
@@ -39,7 +38,7 @@
AsyncSecretsResourceWithStreamingResponse,
)
from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given
-from ..._utils import maybe_transform, async_maybe_transform
+from ..._utils import path_template, maybe_transform, async_maybe_transform
from .sessions import (
SessionsResource,
AsyncSessionsResource,
@@ -132,7 +131,6 @@
)
from ...types.zone_list_response import ZoneListResponse
from ...types.encryption_key_aws_kms_config_param import EncryptionKeyAwsKmsConfigParam
-from ...types.zone_list_session_resource_access_response import ZoneListSessionResourceAccessResponse
__all__ = ["ZonesResource", "AsyncZonesResource"]
@@ -180,7 +178,22 @@ def secrets(self) -> SecretsResource:
@cached_property
def policy_schemas(self) -> PolicySchemasResource:
- """Zone-scoped Cedar schema management"""
+ """Zone-scoped Cedar schema management.
+
+ The Cedar schema defines the entity model used for authorization decisions.
+ Key entity types and their attributes:
+
+ - **Keycard::User** — `email` (String), `groups` (Set of String)
+ - **Keycard::Application** — `registration_method` (RegistrationMethod entity), `credential_type` (CredentialType entity)
+ - **Keycard::RegistrationMethod** — enum entity: `"managed"`, `"dcr"`
+ - **Keycard::CredentialType** — enum entity: `"token"`, `"password"`, `"public-key"`, `"url"`, `"public"`
+ - **Keycard::Resource** — `id` (String), `name` (String), `scopes` (Set of String)
+ - **Keycard::Claims** — `email` (String), `groups` (Set of String), plus arbitrary additional fields
+
+ Enum-like attributes use Cedar enum entity types (schema version `2026-03-16`+).
+ In policies, reference values as `RegistrationMethod::"managed"` or `CredentialType::"token"`.
+ See the Credentials API spec for the full entity model reference.
+ """
return PolicySchemasResource(self._client)
@cached_property
@@ -307,7 +320,7 @@ def retrieve(
if not zone_id:
raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
return self._get(
- f"/zones/{zone_id}",
+ path_template("/zones/{zone_id}", zone_id=zone_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -323,6 +336,7 @@ def update(
zone_id: str,
*,
default_mcp_gateway_application_id: Optional[str] | Omit = omit,
+ default_resource_id: Optional[str] | Omit = omit,
description: Optional[str] | Omit = omit,
encryption_key: Optional[zone_update_params.EncryptionKey] | Omit = omit,
login_flow: Optional[Literal["default", "identifier_first"]] | Omit = omit,
@@ -344,6 +358,9 @@ def update(
default_mcp_gateway_application_id: Application ID configured as the default MCP Gateway for the zone (set to null
to unset)
+ default_resource_id: Resource ID to configure as the default resource for the zone (set to null to
+ unset)
+
description: Human-readable description
encryption_key: AWS KMS configuration for zone encryption update (set to null to remove
@@ -373,10 +390,11 @@ def update(
if not zone_id:
raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
return self._patch(
- f"/zones/{zone_id}",
+ path_template("/zones/{zone_id}", zone_id=zone_id),
body=maybe_transform(
{
"default_mcp_gateway_application_id": default_mcp_gateway_application_id,
+ "default_resource_id": default_resource_id,
"description": description,
"encryption_key": encryption_key,
"login_flow": login_flow,
@@ -476,90 +494,13 @@ def delete(
raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
return self._delete(
- f"/zones/{zone_id}",
+ path_template("/zones/{zone_id}", zone_id=zone_id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
cast_to=NoneType,
)
- def list_session_resource_access(
- self,
- zone_id: str,
- *,
- after: str | Omit = omit,
- before: str | Omit = omit,
- expand: Union[Literal["total_count"], List[Literal["total_count"]]] | Omit = omit,
- limit: int | Omit = omit,
- resource_id: str | Omit = omit,
- rollup_children: bool | Omit = omit,
- session_id: str | Omit = omit,
- user_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.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> ZoneListSessionResourceAccessResponse:
- """Returns aggregated access records per session-resource pair.
-
- By default
- (rollup_children=true), includes access from descendant sessions. Set
- rollup_children=false to return only direct session access. At least one of
- user_id, session_id, or resource_id must be provided.
-
- Args:
- after: Cursor for forward pagination
-
- before: Cursor for backward pagination
-
- limit: Maximum number of items to return
-
- resource_id: Filter by resource ID
-
- rollup_children: Include resource access from descendant sessions. When true (default),
- aggregates access from the session and all its descendants. When false, returns
- only direct access for the session.
-
- session_id: Filter by session ID
-
- user_id: Filter by user ID
-
- extra_headers: Send extra headers
-
- extra_query: Add additional query parameters to the request
-
- extra_body: Add additional JSON properties to the request
-
- timeout: Override the client-level default timeout for this request, in seconds
- """
- if not zone_id:
- raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
- return self._get(
- f"/zones/{zone_id}/session-resource-access",
- options=make_request_options(
- extra_headers=extra_headers,
- extra_query=extra_query,
- extra_body=extra_body,
- timeout=timeout,
- query=maybe_transform(
- {
- "after": after,
- "before": before,
- "expand": expand,
- "limit": limit,
- "resource_id": resource_id,
- "rollup_children": rollup_children,
- "session_id": session_id,
- "user_id": user_id,
- },
- zone_list_session_resource_access_params.ZoneListSessionResourceAccessParams,
- ),
- ),
- cast_to=ZoneListSessionResourceAccessResponse,
- )
-
class AsyncZonesResource(AsyncAPIResource):
@cached_property
@@ -604,7 +545,22 @@ def secrets(self) -> AsyncSecretsResource:
@cached_property
def policy_schemas(self) -> AsyncPolicySchemasResource:
- """Zone-scoped Cedar schema management"""
+ """Zone-scoped Cedar schema management.
+
+ The Cedar schema defines the entity model used for authorization decisions.
+ Key entity types and their attributes:
+
+ - **Keycard::User** — `email` (String), `groups` (Set of String)
+ - **Keycard::Application** — `registration_method` (RegistrationMethod entity), `credential_type` (CredentialType entity)
+ - **Keycard::RegistrationMethod** — enum entity: `"managed"`, `"dcr"`
+ - **Keycard::CredentialType** — enum entity: `"token"`, `"password"`, `"public-key"`, `"url"`, `"public"`
+ - **Keycard::Resource** — `id` (String), `name` (String), `scopes` (Set of String)
+ - **Keycard::Claims** — `email` (String), `groups` (Set of String), plus arbitrary additional fields
+
+ Enum-like attributes use Cedar enum entity types (schema version `2026-03-16`+).
+ In policies, reference values as `RegistrationMethod::"managed"` or `CredentialType::"token"`.
+ See the Credentials API spec for the full entity model reference.
+ """
return AsyncPolicySchemasResource(self._client)
@cached_property
@@ -731,7 +687,7 @@ async def retrieve(
if not zone_id:
raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
return await self._get(
- f"/zones/{zone_id}",
+ path_template("/zones/{zone_id}", zone_id=zone_id),
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -747,6 +703,7 @@ async def update(
zone_id: str,
*,
default_mcp_gateway_application_id: Optional[str] | Omit = omit,
+ default_resource_id: Optional[str] | Omit = omit,
description: Optional[str] | Omit = omit,
encryption_key: Optional[zone_update_params.EncryptionKey] | Omit = omit,
login_flow: Optional[Literal["default", "identifier_first"]] | Omit = omit,
@@ -768,6 +725,9 @@ async def update(
default_mcp_gateway_application_id: Application ID configured as the default MCP Gateway for the zone (set to null
to unset)
+ default_resource_id: Resource ID to configure as the default resource for the zone (set to null to
+ unset)
+
description: Human-readable description
encryption_key: AWS KMS configuration for zone encryption update (set to null to remove
@@ -797,10 +757,11 @@ async def update(
if not zone_id:
raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
return await self._patch(
- f"/zones/{zone_id}",
+ path_template("/zones/{zone_id}", zone_id=zone_id),
body=await async_maybe_transform(
{
"default_mcp_gateway_application_id": default_mcp_gateway_application_id,
+ "default_resource_id": default_resource_id,
"description": description,
"encryption_key": encryption_key,
"login_flow": login_flow,
@@ -900,90 +861,13 @@ async def delete(
raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
extra_headers = {"Accept": "*/*", **(extra_headers or {})}
return await self._delete(
- f"/zones/{zone_id}",
+ path_template("/zones/{zone_id}", zone_id=zone_id),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
cast_to=NoneType,
)
- async def list_session_resource_access(
- self,
- zone_id: str,
- *,
- after: str | Omit = omit,
- before: str | Omit = omit,
- expand: Union[Literal["total_count"], List[Literal["total_count"]]] | Omit = omit,
- limit: int | Omit = omit,
- resource_id: str | Omit = omit,
- rollup_children: bool | Omit = omit,
- session_id: str | Omit = omit,
- user_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.
- extra_headers: Headers | None = None,
- extra_query: Query | None = None,
- extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = not_given,
- ) -> ZoneListSessionResourceAccessResponse:
- """Returns aggregated access records per session-resource pair.
-
- By default
- (rollup_children=true), includes access from descendant sessions. Set
- rollup_children=false to return only direct session access. At least one of
- user_id, session_id, or resource_id must be provided.
-
- Args:
- after: Cursor for forward pagination
-
- before: Cursor for backward pagination
-
- limit: Maximum number of items to return
-
- resource_id: Filter by resource ID
-
- rollup_children: Include resource access from descendant sessions. When true (default),
- aggregates access from the session and all its descendants. When false, returns
- only direct access for the session.
-
- session_id: Filter by session ID
-
- user_id: Filter by user ID
-
- extra_headers: Send extra headers
-
- extra_query: Add additional query parameters to the request
-
- extra_body: Add additional JSON properties to the request
-
- timeout: Override the client-level default timeout for this request, in seconds
- """
- if not zone_id:
- raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
- return await self._get(
- f"/zones/{zone_id}/session-resource-access",
- options=make_request_options(
- extra_headers=extra_headers,
- extra_query=extra_query,
- extra_body=extra_body,
- timeout=timeout,
- query=await async_maybe_transform(
- {
- "after": after,
- "before": before,
- "expand": expand,
- "limit": limit,
- "resource_id": resource_id,
- "rollup_children": rollup_children,
- "session_id": session_id,
- "user_id": user_id,
- },
- zone_list_session_resource_access_params.ZoneListSessionResourceAccessParams,
- ),
- ),
- cast_to=ZoneListSessionResourceAccessResponse,
- )
-
class ZonesResourceWithRawResponse:
def __init__(self, zones: ZonesResource) -> None:
@@ -1004,9 +888,6 @@ def __init__(self, zones: ZonesResource) -> None:
self.delete = to_raw_response_wrapper(
zones.delete,
)
- self.list_session_resource_access = to_raw_response_wrapper(
- zones.list_session_resource_access,
- )
@cached_property
def applications(self) -> ApplicationsResourceWithRawResponse:
@@ -1050,7 +931,22 @@ def secrets(self) -> SecretsResourceWithRawResponse:
@cached_property
def policy_schemas(self) -> PolicySchemasResourceWithRawResponse:
- """Zone-scoped Cedar schema management"""
+ """Zone-scoped Cedar schema management.
+
+ The Cedar schema defines the entity model used for authorization decisions.
+ Key entity types and their attributes:
+
+ - **Keycard::User** — `email` (String), `groups` (Set of String)
+ - **Keycard::Application** — `registration_method` (RegistrationMethod entity), `credential_type` (CredentialType entity)
+ - **Keycard::RegistrationMethod** — enum entity: `"managed"`, `"dcr"`
+ - **Keycard::CredentialType** — enum entity: `"token"`, `"password"`, `"public-key"`, `"url"`, `"public"`
+ - **Keycard::Resource** — `id` (String), `name` (String), `scopes` (Set of String)
+ - **Keycard::Claims** — `email` (String), `groups` (Set of String), plus arbitrary additional fields
+
+ Enum-like attributes use Cedar enum entity types (schema version `2026-03-16`+).
+ In policies, reference values as `RegistrationMethod::"managed"` or `CredentialType::"token"`.
+ See the Credentials API spec for the full entity model reference.
+ """
return PolicySchemasResourceWithRawResponse(self._zones.policy_schemas)
@cached_property
@@ -1083,9 +979,6 @@ def __init__(self, zones: AsyncZonesResource) -> None:
self.delete = async_to_raw_response_wrapper(
zones.delete,
)
- self.list_session_resource_access = async_to_raw_response_wrapper(
- zones.list_session_resource_access,
- )
@cached_property
def applications(self) -> AsyncApplicationsResourceWithRawResponse:
@@ -1129,7 +1022,22 @@ def secrets(self) -> AsyncSecretsResourceWithRawResponse:
@cached_property
def policy_schemas(self) -> AsyncPolicySchemasResourceWithRawResponse:
- """Zone-scoped Cedar schema management"""
+ """Zone-scoped Cedar schema management.
+
+ The Cedar schema defines the entity model used for authorization decisions.
+ Key entity types and their attributes:
+
+ - **Keycard::User** — `email` (String), `groups` (Set of String)
+ - **Keycard::Application** — `registration_method` (RegistrationMethod entity), `credential_type` (CredentialType entity)
+ - **Keycard::RegistrationMethod** — enum entity: `"managed"`, `"dcr"`
+ - **Keycard::CredentialType** — enum entity: `"token"`, `"password"`, `"public-key"`, `"url"`, `"public"`
+ - **Keycard::Resource** — `id` (String), `name` (String), `scopes` (Set of String)
+ - **Keycard::Claims** — `email` (String), `groups` (Set of String), plus arbitrary additional fields
+
+ Enum-like attributes use Cedar enum entity types (schema version `2026-03-16`+).
+ In policies, reference values as `RegistrationMethod::"managed"` or `CredentialType::"token"`.
+ See the Credentials API spec for the full entity model reference.
+ """
return AsyncPolicySchemasResourceWithRawResponse(self._zones.policy_schemas)
@cached_property
@@ -1162,9 +1070,6 @@ def __init__(self, zones: ZonesResource) -> None:
self.delete = to_streamed_response_wrapper(
zones.delete,
)
- self.list_session_resource_access = to_streamed_response_wrapper(
- zones.list_session_resource_access,
- )
@cached_property
def applications(self) -> ApplicationsResourceWithStreamingResponse:
@@ -1208,7 +1113,22 @@ def secrets(self) -> SecretsResourceWithStreamingResponse:
@cached_property
def policy_schemas(self) -> PolicySchemasResourceWithStreamingResponse:
- """Zone-scoped Cedar schema management"""
+ """Zone-scoped Cedar schema management.
+
+ The Cedar schema defines the entity model used for authorization decisions.
+ Key entity types and their attributes:
+
+ - **Keycard::User** — `email` (String), `groups` (Set of String)
+ - **Keycard::Application** — `registration_method` (RegistrationMethod entity), `credential_type` (CredentialType entity)
+ - **Keycard::RegistrationMethod** — enum entity: `"managed"`, `"dcr"`
+ - **Keycard::CredentialType** — enum entity: `"token"`, `"password"`, `"public-key"`, `"url"`, `"public"`
+ - **Keycard::Resource** — `id` (String), `name` (String), `scopes` (Set of String)
+ - **Keycard::Claims** — `email` (String), `groups` (Set of String), plus arbitrary additional fields
+
+ Enum-like attributes use Cedar enum entity types (schema version `2026-03-16`+).
+ In policies, reference values as `RegistrationMethod::"managed"` or `CredentialType::"token"`.
+ See the Credentials API spec for the full entity model reference.
+ """
return PolicySchemasResourceWithStreamingResponse(self._zones.policy_schemas)
@cached_property
@@ -1241,9 +1161,6 @@ def __init__(self, zones: AsyncZonesResource) -> None:
self.delete = async_to_streamed_response_wrapper(
zones.delete,
)
- self.list_session_resource_access = async_to_streamed_response_wrapper(
- zones.list_session_resource_access,
- )
@cached_property
def applications(self) -> AsyncApplicationsResourceWithStreamingResponse:
@@ -1287,7 +1204,22 @@ def secrets(self) -> AsyncSecretsResourceWithStreamingResponse:
@cached_property
def policy_schemas(self) -> AsyncPolicySchemasResourceWithStreamingResponse:
- """Zone-scoped Cedar schema management"""
+ """Zone-scoped Cedar schema management.
+
+ The Cedar schema defines the entity model used for authorization decisions.
+ Key entity types and their attributes:
+
+ - **Keycard::User** — `email` (String), `groups` (Set of String)
+ - **Keycard::Application** — `registration_method` (RegistrationMethod entity), `credential_type` (CredentialType entity)
+ - **Keycard::RegistrationMethod** — enum entity: `"managed"`, `"dcr"`
+ - **Keycard::CredentialType** — enum entity: `"token"`, `"password"`, `"public-key"`, `"url"`, `"public"`
+ - **Keycard::Resource** — `id` (String), `name` (String), `scopes` (Set of String)
+ - **Keycard::Claims** — `email` (String), `groups` (Set of String), plus arbitrary additional fields
+
+ Enum-like attributes use Cedar enum entity types (schema version `2026-03-16`+).
+ In policies, reference values as `RegistrationMethod::"managed"` or `CredentialType::"token"`.
+ See the Credentials API spec for the full entity model reference.
+ """
return AsyncPolicySchemasResourceWithStreamingResponse(self._zones.policy_schemas)
@cached_property
diff --git a/src/keycardai_api/types/__init__.py b/src/keycardai_api/types/__init__.py
index 3d22585..d89e8f9 100644
--- a/src/keycardai_api/types/__init__.py
+++ b/src/keycardai_api/types/__init__.py
@@ -28,9 +28,3 @@
from .organization_list_identities_response import (
OrganizationListIdentitiesResponse as OrganizationListIdentitiesResponse,
)
-from .zone_list_session_resource_access_params import (
- ZoneListSessionResourceAccessParams as ZoneListSessionResourceAccessParams,
-)
-from .zone_list_session_resource_access_response import (
- ZoneListSessionResourceAccessResponse as ZoneListSessionResourceAccessResponse,
-)
diff --git a/src/keycardai_api/types/organization_list_identities_params.py b/src/keycardai_api/types/organization_list_identities_params.py
index 80f52b6..f0bc729 100644
--- a/src/keycardai_api/types/organization_list_identities_params.py
+++ b/src/keycardai_api/types/organization_list_identities_params.py
@@ -18,16 +18,22 @@ class OrganizationListIdentitiesParams(TypedDict, total=False):
before: str
"""Cursor for backward pagination"""
- expand: List[Literal["permissions"]]
+ expand: List[Literal["permissions", "total_count"]]
"""Fields to expand in the response.
- Currently supports "permissions" to include the permissions field with the
- caller's permissions for the resource.
+ Supports "permissions" to include the permissions field with the caller's
+ permissions for the resource. For list organization identities only,
+ "total_count" populates pagination.total_count with the number of identities
+ matching the same filters as the list (excluding cursor and limit). Other
+ operations ignore expand values they do not use.
"""
limit: int
"""Maximum number of identities to return"""
+ query_email: Annotated[str, PropertyInfo(alias="query[email]")]
+ """Search identities by email substring (case-insensitive)"""
+
role: OrganizationRole
"""Filter identities by role"""
diff --git a/src/keycardai_api/types/organization_list_identities_response.py b/src/keycardai_api/types/organization_list_identities_response.py
index c990b44..d42fd55 100644
--- a/src/keycardai_api/types/organization_list_identities_response.py
+++ b/src/keycardai_api/types/organization_list_identities_response.py
@@ -8,7 +8,7 @@
from .page_info_cursor import PageInfoCursor
from .organizations.organization_role import OrganizationRole
-__all__ = ["OrganizationListIdentitiesResponse", "Item"]
+__all__ = ["OrganizationListIdentitiesResponse", "Item", "Pagination"]
class Item(BaseModel):
@@ -50,6 +50,22 @@ class Item(BaseModel):
"""
+class Pagination(BaseModel):
+ """Cursor-based pagination metadata returned alongside a list of results"""
+
+ after_cursor: str
+ """An opaque cursor used for paginating through a list of results"""
+
+ before_cursor: str
+ """An opaque cursor used for paginating through a list of results"""
+
+ total_count: Optional[int] = None
+ """Total number of items across all pages.
+
+ Only present when the request includes ?expand=total_count.
+ """
+
+
class OrganizationListIdentitiesResponse(BaseModel):
"""List of identities (users and invitations) in an organization"""
@@ -58,6 +74,9 @@ class OrganizationListIdentitiesResponse(BaseModel):
page_info: PageInfoCursor
"""Pagination information using cursor-based pagination"""
+ pagination: Pagination
+ """Cursor-based pagination metadata returned alongside a list of results"""
+
permissions: Optional[Dict[str, Dict[str, bool]]] = None
"""
Permissions granted to the authenticated principal for this resource. Only
diff --git a/src/keycardai_api/types/organization_list_params.py b/src/keycardai_api/types/organization_list_params.py
index b4a9f9d..dd31beb 100644
--- a/src/keycardai_api/types/organization_list_params.py
+++ b/src/keycardai_api/types/organization_list_params.py
@@ -17,11 +17,14 @@ class OrganizationListParams(TypedDict, total=False):
before: str
"""Cursor for backward pagination"""
- expand: List[Literal["permissions"]]
+ expand: List[Literal["permissions", "total_count"]]
"""Fields to expand in the response.
- Currently supports "permissions" to include the permissions field with the
- caller's permissions for the resource.
+ Supports "permissions" to include the permissions field with the caller's
+ permissions for the resource. For list organization identities only,
+ "total_count" populates pagination.total_count with the number of identities
+ matching the same filters as the list (excluding cursor and limit). Other
+ operations ignore expand values they do not use.
"""
limit: int
diff --git a/src/keycardai_api/types/organization_list_roles_params.py b/src/keycardai_api/types/organization_list_roles_params.py
index ab37c20..99b7a70 100644
--- a/src/keycardai_api/types/organization_list_roles_params.py
+++ b/src/keycardai_api/types/organization_list_roles_params.py
@@ -12,11 +12,14 @@
class OrganizationListRolesParams(TypedDict, total=False):
- expand: List[Literal["permissions"]]
+ expand: List[Literal["permissions", "total_count"]]
"""Fields to expand in the response.
- Currently supports "permissions" to include the permissions field with the
- caller's permissions for the resource.
+ Supports "permissions" to include the permissions field with the caller's
+ permissions for the resource. For list organization identities only,
+ "total_count" populates pagination.total_count with the number of identities
+ matching the same filters as the list (excluding cursor and limit). Other
+ operations ignore expand values they do not use.
"""
scope: RoleScope
diff --git a/src/keycardai_api/types/organization_retrieve_params.py b/src/keycardai_api/types/organization_retrieve_params.py
index 3feac27..1a5d525 100644
--- a/src/keycardai_api/types/organization_retrieve_params.py
+++ b/src/keycardai_api/types/organization_retrieve_params.py
@@ -11,11 +11,14 @@
class OrganizationRetrieveParams(TypedDict, total=False):
- expand: List[Literal["permissions"]]
+ expand: List[Literal["permissions", "total_count"]]
"""Fields to expand in the response.
- Currently supports "permissions" to include the permissions field with the
- caller's permissions for the resource.
+ Supports "permissions" to include the permissions field with the caller's
+ permissions for the resource. For list organization identities only,
+ "total_count" populates pagination.total_count with the number of identities
+ matching the same filters as the list (excluding cursor and limit). Other
+ operations ignore expand values they do not use.
"""
x_client_request_id: Annotated[str, PropertyInfo(alias="X-Client-Request-ID")]
diff --git a/src/keycardai_api/types/organizations/invitation_list_params.py b/src/keycardai_api/types/organizations/invitation_list_params.py
index 093ead1..30b5da3 100644
--- a/src/keycardai_api/types/organizations/invitation_list_params.py
+++ b/src/keycardai_api/types/organizations/invitation_list_params.py
@@ -17,11 +17,14 @@ class InvitationListParams(TypedDict, total=False):
before: str
"""Cursor for backward pagination"""
- expand: List[Literal["permissions"]]
+ expand: List[Literal["permissions", "total_count"]]
"""Fields to expand in the response.
- Currently supports "permissions" to include the permissions field with the
- caller's permissions for the resource.
+ Supports "permissions" to include the permissions field with the caller's
+ permissions for the resource. For list organization identities only,
+ "total_count" populates pagination.total_count with the number of identities
+ matching the same filters as the list (excluding cursor and limit). Other
+ operations ignore expand values they do not use.
"""
limit: int
diff --git a/src/keycardai_api/types/organizations/service_account_list_params.py b/src/keycardai_api/types/organizations/service_account_list_params.py
index e3f9d40..7b32b8f 100644
--- a/src/keycardai_api/types/organizations/service_account_list_params.py
+++ b/src/keycardai_api/types/organizations/service_account_list_params.py
@@ -17,11 +17,14 @@ class ServiceAccountListParams(TypedDict, total=False):
before: str
"""Cursor for backward pagination"""
- expand: List[Literal["permissions"]]
+ expand: List[Literal["permissions", "total_count"]]
"""Fields to expand in the response.
- Currently supports "permissions" to include the permissions field with the
- caller's permissions for the resource.
+ Supports "permissions" to include the permissions field with the caller's
+ permissions for the resource. For list organization identities only,
+ "total_count" populates pagination.total_count with the number of identities
+ matching the same filters as the list (excluding cursor and limit). Other
+ operations ignore expand values they do not use.
"""
limit: int
diff --git a/src/keycardai_api/types/organizations/service_account_retrieve_params.py b/src/keycardai_api/types/organizations/service_account_retrieve_params.py
index c138d5d..b3ca28b 100644
--- a/src/keycardai_api/types/organizations/service_account_retrieve_params.py
+++ b/src/keycardai_api/types/organizations/service_account_retrieve_params.py
@@ -14,11 +14,14 @@ class ServiceAccountRetrieveParams(TypedDict, total=False):
organization_id: Required[str]
"""Organization ID or label identifier"""
- expand: List[Literal["permissions"]]
+ expand: List[Literal["permissions", "total_count"]]
"""Fields to expand in the response.
- Currently supports "permissions" to include the permissions field with the
- caller's permissions for the resource.
+ Supports "permissions" to include the permissions field with the caller's
+ permissions for the resource. For list organization identities only,
+ "total_count" populates pagination.total_count with the number of identities
+ matching the same filters as the list (excluding cursor and limit). Other
+ operations ignore expand values they do not use.
"""
x_client_request_id: Annotated[str, PropertyInfo(alias="X-Client-Request-ID")]
diff --git a/src/keycardai_api/types/organizations/service_accounts/credential_list_params.py b/src/keycardai_api/types/organizations/service_accounts/credential_list_params.py
index 7fb0efe..7843e64 100644
--- a/src/keycardai_api/types/organizations/service_accounts/credential_list_params.py
+++ b/src/keycardai_api/types/organizations/service_accounts/credential_list_params.py
@@ -20,11 +20,14 @@ class CredentialListParams(TypedDict, total=False):
before: str
"""Cursor for backward pagination"""
- expand: List[Literal["permissions"]]
+ expand: List[Literal["permissions", "total_count"]]
"""Fields to expand in the response.
- Currently supports "permissions" to include the permissions field with the
- caller's permissions for the resource.
+ Supports "permissions" to include the permissions field with the caller's
+ permissions for the resource. For list organization identities only,
+ "total_count" populates pagination.total_count with the number of identities
+ matching the same filters as the list (excluding cursor and limit). Other
+ operations ignore expand values they do not use.
"""
limit: int
diff --git a/src/keycardai_api/types/organizations/service_accounts/credential_retrieve_params.py b/src/keycardai_api/types/organizations/service_accounts/credential_retrieve_params.py
index 68ed360..e9ebe55 100644
--- a/src/keycardai_api/types/organizations/service_accounts/credential_retrieve_params.py
+++ b/src/keycardai_api/types/organizations/service_accounts/credential_retrieve_params.py
@@ -17,11 +17,14 @@ class CredentialRetrieveParams(TypedDict, total=False):
service_account_id: Required[str]
"""Identifier for API resources. A 26-char nanoid (URL/DNS safe)."""
- expand: List[Literal["permissions"]]
+ expand: List[Literal["permissions", "total_count"]]
"""Fields to expand in the response.
- Currently supports "permissions" to include the permissions field with the
- caller's permissions for the resource.
+ Supports "permissions" to include the permissions field with the caller's
+ permissions for the resource. For list organization identities only,
+ "total_count" populates pagination.total_count with the number of identities
+ matching the same filters as the list (excluding cursor and limit). Other
+ operations ignore expand values they do not use.
"""
x_client_request_id: Annotated[str, PropertyInfo(alias="X-Client-Request-ID")]
diff --git a/src/keycardai_api/types/organizations/sso_connection_retrieve_params.py b/src/keycardai_api/types/organizations/sso_connection_retrieve_params.py
index bacc08e..63c48c6 100644
--- a/src/keycardai_api/types/organizations/sso_connection_retrieve_params.py
+++ b/src/keycardai_api/types/organizations/sso_connection_retrieve_params.py
@@ -11,11 +11,14 @@
class SSOConnectionRetrieveParams(TypedDict, total=False):
- expand: List[Literal["permissions"]]
+ expand: List[Literal["permissions", "total_count"]]
"""Fields to expand in the response.
- Currently supports "permissions" to include the permissions field with the
- caller's permissions for the resource.
+ Supports "permissions" to include the permissions field with the caller's
+ permissions for the resource. For list organization identities only,
+ "total_count" populates pagination.total_count with the number of identities
+ matching the same filters as the list (excluding cursor and limit). Other
+ operations ignore expand values they do not use.
"""
x_client_request_id: Annotated[str, PropertyInfo(alias="X-Client-Request-ID")]
diff --git a/src/keycardai_api/types/organizations/user_list_params.py b/src/keycardai_api/types/organizations/user_list_params.py
index 9860ad7..00cd32f 100644
--- a/src/keycardai_api/types/organizations/user_list_params.py
+++ b/src/keycardai_api/types/organizations/user_list_params.py
@@ -18,11 +18,14 @@ class UserListParams(TypedDict, total=False):
before: str
"""Cursor for backward pagination"""
- expand: List[Literal["permissions"]]
+ expand: List[Literal["permissions", "total_count"]]
"""Fields to expand in the response.
- Currently supports "permissions" to include the permissions field with the
- caller's permissions for the resource.
+ Supports "permissions" to include the permissions field with the caller's
+ permissions for the resource. For list organization identities only,
+ "total_count" populates pagination.total_count with the number of identities
+ matching the same filters as the list (excluding cursor and limit). Other
+ operations ignore expand values they do not use.
"""
limit: int
diff --git a/src/keycardai_api/types/organizations/user_retrieve_params.py b/src/keycardai_api/types/organizations/user_retrieve_params.py
index 9f8ce63..001ce54 100644
--- a/src/keycardai_api/types/organizations/user_retrieve_params.py
+++ b/src/keycardai_api/types/organizations/user_retrieve_params.py
@@ -14,11 +14,14 @@ class UserRetrieveParams(TypedDict, total=False):
organization_id: Required[str]
"""Organization ID or label identifier"""
- expand: List[Literal["permissions"]]
+ expand: List[Literal["permissions", "total_count"]]
"""Fields to expand in the response.
- Currently supports "permissions" to include the permissions field with the
- caller's permissions for the resource.
+ Supports "permissions" to include the permissions field with the caller's
+ permissions for the resource. For list organization identities only,
+ "total_count" populates pagination.total_count with the number of identities
+ matching the same filters as the list (excluding cursor and limit). Other
+ operations ignore expand values they do not use.
"""
x_client_request_id: Annotated[str, PropertyInfo(alias="X-Client-Request-ID")]
diff --git a/src/keycardai_api/types/zone.py b/src/keycardai_api/types/zone.py
index cf3849d..95e1c85 100644
--- a/src/keycardai_api/types/zone.py
+++ b/src/keycardai_api/types/zone.py
@@ -94,6 +94,9 @@ class Zone(BaseModel):
default_mcp_gateway_application_id: Optional[str] = None
"""Application ID configured as the default MCP Gateway for the zone"""
+ default_resource_id: Optional[str] = None
+ """Resource ID configured as the default resource for the zone"""
+
description: Optional[str] = None
"""Human-readable description"""
diff --git a/src/keycardai_api/types/zone_list_session_resource_access_params.py b/src/keycardai_api/types/zone_list_session_resource_access_params.py
deleted file mode 100644
index 2139950..0000000
--- a/src/keycardai_api/types/zone_list_session_resource_access_params.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from __future__ import annotations
-
-from typing import List, Union
-from typing_extensions import Literal, Annotated, TypedDict
-
-from .._utils import PropertyInfo
-
-__all__ = ["ZoneListSessionResourceAccessParams"]
-
-
-class ZoneListSessionResourceAccessParams(TypedDict, total=False):
- after: str
- """Cursor for forward pagination"""
-
- before: str
- """Cursor for backward pagination"""
-
- expand: Annotated[Union[Literal["total_count"], List[Literal["total_count"]]], PropertyInfo(alias="expand[]")]
-
- limit: int
- """Maximum number of items to return"""
-
- resource_id: str
- """Filter by resource ID"""
-
- rollup_children: bool
- """Include resource access from descendant sessions.
-
- When true (default), aggregates access from the session and all its descendants.
- When false, returns only direct access for the session.
- """
-
- session_id: str
- """Filter by session ID"""
-
- user_id: str
- """Filter by user ID"""
diff --git a/src/keycardai_api/types/zone_list_session_resource_access_response.py b/src/keycardai_api/types/zone_list_session_resource_access_response.py
deleted file mode 100644
index dd0d77f..0000000
--- a/src/keycardai_api/types/zone_list_session_resource_access_response.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from typing import List, Optional
-from datetime import datetime
-
-from .._models import BaseModel
-
-__all__ = ["ZoneListSessionResourceAccessResponse", "Item", "Pagination"]
-
-
-class Item(BaseModel):
- """Aggregated record of session-resource access events"""
-
- first_accessed_at: datetime
- """When access first occurred"""
-
- last_accessed_at: datetime
- """When access most recently occurred"""
-
- organization_id: str
- """Organization ID"""
-
- resource_id: str
- """Resource ID"""
-
- session_id: str
- """Session ID"""
-
- total_access_count: int
- """Total number of access events for this session-resource pair"""
-
-
-class Pagination(BaseModel):
- """Cursor-based pagination metadata"""
-
- after_cursor: Optional[str] = None
- """An opaque cursor used for paginating through a list of results"""
-
- before_cursor: Optional[str] = None
- """An opaque cursor used for paginating through a list of results"""
-
- total_count: Optional[int] = None
- """Total number of items matching the query.
-
- Only included when expand[]=total_count is requested.
- """
-
-
-class ZoneListSessionResourceAccessResponse(BaseModel):
- items: List[Item]
-
- pagination: Pagination
- """Cursor-based pagination metadata"""
diff --git a/src/keycardai_api/types/zone_update_params.py b/src/keycardai_api/types/zone_update_params.py
index 14abf0a..352d679 100644
--- a/src/keycardai_api/types/zone_update_params.py
+++ b/src/keycardai_api/types/zone_update_params.py
@@ -15,6 +15,12 @@ class ZoneUpdateParams(TypedDict, total=False):
to unset)
"""
+ default_resource_id: Optional[str]
+ """
+ Resource ID to configure as the default resource for the zone (set to null to
+ unset)
+ """
+
description: Optional[str]
"""Human-readable description"""
diff --git a/src/keycardai_api/types/zones/__init__.py b/src/keycardai_api/types/zones/__init__.py
index e32d0c2..283dad0 100644
--- a/src/keycardai_api/types/zones/__init__.py
+++ b/src/keycardai_api/types/zones/__init__.py
@@ -19,7 +19,6 @@
from .public_key import PublicKey as PublicKey
from .user_agent import UserAgent as UserAgent
from .application import Application as Application
-from .attestation import Attestation as Attestation
from .base_fields import BaseFields as BaseFields
from .zone_member import ZoneMember as ZoneMember
from .metadata_param import MetadataParam as MetadataParam
@@ -44,6 +43,7 @@
from .secret_create_params import SecretCreateParams as SecretCreateParams
from .secret_list_response import SecretListResponse as SecretListResponse
from .secret_update_params import SecretUpdateParams as SecretUpdateParams
+from .attestation_statement import AttestationStatement as AttestationStatement
from .metadata_update_param import MetadataUpdateParam as MetadataUpdateParam
from .session_list_response import SessionListResponse as SessionListResponse
from .session_update_params import SessionUpdateParams as SessionUpdateParams
diff --git a/src/keycardai_api/types/zones/attestation.py b/src/keycardai_api/types/zones/attestation.py
deleted file mode 100644
index cf0c50a..0000000
--- a/src/keycardai_api/types/zones/attestation.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from ..._models import BaseModel
-
-__all__ = ["Attestation"]
-
-
-class Attestation(BaseModel):
- """JWS Flattened JSON Serialization (RFC 7515 §7.2.2) of a policy set attestation.
-
- The protected header carries the signing algorithm and key identifier; the payload is a base64url-encoded AttestationStatement canonicalized per RFC 8785 (JCS). Verify using the zone JWKS endpoint (RFC 7517). Currently signed with RS256; future zone key types (e.g. EdDSA) will be indicated by the "alg" header — no envelope changes required.
- """
-
- payload: str
- """Base64url-encoded AttestationStatement (RFC 7515 §3).
-
- Decode to inspect attestation content. The RFC 8785 canonical form of the
- decoded JSON is the JWS Signing Input alongside the protected header.
- """
-
- protected: str
- """Base64url-encoded JWS protected header (RFC 7515 §4).
-
- Contains at minimum "alg" (signing algorithm — currently RS256, will migrate to
- EdDSA) and "kid" (signing key identifier resolvable via the zone JWKS endpoint).
- """
-
- signature: str
- """
- Base64url-encoded digital signature computed over the JWS Signing Input
- (ASCII(protected) || '.' || payload) per RFC 7515 §5.1.
- """
diff --git a/src/keycardai_api/types/zones/attestation_statement.py b/src/keycardai_api/types/zones/attestation_statement.py
new file mode 100644
index 0000000..c83758a
--- /dev/null
+++ b/src/keycardai_api/types/zones/attestation_statement.py
@@ -0,0 +1,51 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from datetime import datetime
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["AttestationStatement"]
+
+
+class AttestationStatement(BaseModel):
+ """Decoded content of an Attestation JWS payload.
+
+ Describes the exact policy set version composition at attestation time. This schema defines what consumers see after base64url-decoding the Attestation.payload field.
+ """
+
+ attested_at: datetime
+
+ attested_by: str
+
+ key_id: str
+ """Key ID of the signing key used to produce the attestation signature.
+
+ Matches the "kid" in the JWS protected header.
+ """
+
+ manifest_sha: str
+ """SHA-256 of the policy set version manifest.
+
+ Verifiers MUST check this matches the policy_set_version.manifest_sha to detect
+ attestation/version mismatches.
+ """
+
+ policy_set_id: str
+
+ policy_set_version: int
+
+ status: Literal["created", "re_signed"]
+ """Event that produced this attestation.
+
+ "created" is the initial attestation at version creation; "re_signed" is a
+ re-attestation after key rotation (same content, new signature).
+ """
+
+ type: Literal["policy_set_attestation"]
+ """Statement type discriminator"""
+
+ v: Literal[1]
+ """Statement schema version"""
+
+ zone_id: str
diff --git a/src/keycardai_api/types/zones/policies/policy_version.py b/src/keycardai_api/types/zones/policies/policy_version.py
index e16b24e..d4c1c06 100644
--- a/src/keycardai_api/types/zones/policies/policy_version.py
+++ b/src/keycardai_api/types/zones/policies/policy_version.py
@@ -2,6 +2,7 @@
from typing import Optional
from datetime import datetime
+from typing_extensions import Literal
from ...._models import BaseModel
@@ -15,9 +16,17 @@ class PolicyVersion(BaseModel):
created_by: str
+ owner_type: Literal["platform", "customer"]
+ """Who manages this policy version:
+
+ - `"platform"` — managed by the Keycard platform (system policy versions).
+ - `"customer"` — managed by the tenant (custom policy versions).
+ """
+
policy_id: str
schema_version: str
+ """Schema version this policy was validated against when created."""
sha: str
"""Hex-encoded content hash"""
diff --git a/src/keycardai_api/types/zones/policies/version_create_params.py b/src/keycardai_api/types/zones/policies/version_create_params.py
index 3c36f37..96d245c 100644
--- a/src/keycardai_api/types/zones/policies/version_create_params.py
+++ b/src/keycardai_api/types/zones/policies/version_create_params.py
@@ -14,6 +14,7 @@ class VersionCreateParams(TypedDict, total=False):
zone_id: Required[str]
schema_version: Required[str]
+ """Schema version to validate this policy against. Must not be archived."""
cedar_json: Optional[object]
"""Cedar policy in JSON representation. Mutually exclusive with cedar_raw."""
diff --git a/src/keycardai_api/types/zones/policy.py b/src/keycardai_api/types/zones/policy.py
index 6119e97..a542719 100644
--- a/src/keycardai_api/types/zones/policy.py
+++ b/src/keycardai_api/types/zones/policy.py
@@ -19,6 +19,11 @@ class Policy(BaseModel):
name: str
owner_type: Literal["platform", "customer"]
+ """Who manages this policy:
+
+ - `"platform"` — managed by the Keycard platform (system policies).
+ - `"customer"` — managed by the tenant (custom policies).
+ """
updated_at: datetime
diff --git a/src/keycardai_api/types/zones/policy_set.py b/src/keycardai_api/types/zones/policy_set.py
index a8cd4b6..5436db8 100644
--- a/src/keycardai_api/types/zones/policy_set.py
+++ b/src/keycardai_api/types/zones/policy_set.py
@@ -19,8 +19,20 @@ class PolicySet(BaseModel):
name: str
owner_type: Literal["platform", "customer"]
+ """Who manages this policy set:
+
+ - `"platform"` — managed by the Keycard platform (system policies).
+ - `"customer"` — managed by the tenant (custom policies).
+ """
scope_type: Literal["zone", "resource", "user", "session"]
+ """The scope at which this policy set applies:
+
+ - `"zone"` — applies to all requests in the zone.
+ - `"resource"` — scoped to a specific resource.
+ - `"user"` — scoped to a specific user.
+ - `"session"` — scoped to a specific session.
+ """
updated_at: datetime
diff --git a/src/keycardai_api/types/zones/policy_set_create_params.py b/src/keycardai_api/types/zones/policy_set_create_params.py
index b7b9996..17a1c63 100644
--- a/src/keycardai_api/types/zones/policy_set_create_params.py
+++ b/src/keycardai_api/types/zones/policy_set_create_params.py
@@ -13,6 +13,13 @@ class PolicySetCreateParams(TypedDict, total=False):
name: Required[str]
scope_type: Literal["zone", "resource", "user", "session"]
+ """The scope at which this policy set applies:
+
+ - `"zone"` — applies to all requests in the zone.
+ - `"resource"` — scoped to a specific resource.
+ - `"user"` — scoped to a specific user.
+ - `"session"` — scoped to a specific session.
+ """
x_api_version: Annotated[str, PropertyInfo(alias="X-API-Version")]
diff --git a/src/keycardai_api/types/zones/policy_sets/policy_set_version.py b/src/keycardai_api/types/zones/policy_sets/policy_set_version.py
index d04ec8a..5b89efc 100644
--- a/src/keycardai_api/types/zones/policy_sets/policy_set_version.py
+++ b/src/keycardai_api/types/zones/policy_sets/policy_set_version.py
@@ -2,10 +2,11 @@
from typing import Optional
from datetime import datetime
+from typing_extensions import Literal
from ...._models import BaseModel
-from ..attestation import Attestation
from ..policy_set_manifest import PolicySetManifest
+from ..attestation_statement import AttestationStatement
__all__ = ["PolicySetVersion"]
@@ -22,9 +23,20 @@ class PolicySetVersion(BaseModel):
manifest_sha: str
"""Hex-encoded SHA-256 of the canonicalized manifest"""
+ owner_type: Literal["platform", "customer"]
+ """Who manages this policy set version:
+
+ - `"platform"` — managed by the Keycard platform (system policy set versions).
+ - `"customer"` — managed by the tenant (custom policy set versions).
+ """
+
policy_set_id: str
schema_version: str
+ """Schema version pinned to this policy set version.
+
+ Determines the Cedar schema used for evaluation when activated.
+ """
version: int
@@ -35,12 +47,10 @@ class PolicySetVersion(BaseModel):
archived_by: Optional[str] = None
- attestation: Optional[Attestation] = None
- """JWS Flattened JSON Serialization (RFC 7515 §7.2.2) of a policy set attestation.
+ attestation: Optional[AttestationStatement] = None
+ """Decoded content of an Attestation JWS payload.
- The protected header carries the signing algorithm and key identifier; the
- payload is a base64url-encoded AttestationStatement canonicalized per RFC 8785
- (JCS). Verify using the zone JWKS endpoint (RFC 7517). Currently signed with
- RS256; future zone key types (e.g. EdDSA) will be indicated by the "alg" header
- — no envelope changes required.
+ Describes the exact policy set version composition at attestation time. This
+ schema defines what consumers see after base64url-decoding the
+ Attestation.payload field.
"""
diff --git a/src/keycardai_api/types/zones/policy_sets/version_create_params.py b/src/keycardai_api/types/zones/policy_sets/version_create_params.py
index d5c2bc8..b381343 100644
--- a/src/keycardai_api/types/zones/policy_sets/version_create_params.py
+++ b/src/keycardai_api/types/zones/policy_sets/version_create_params.py
@@ -16,6 +16,7 @@ class VersionCreateParams(TypedDict, total=False):
manifest: Required[PolicySetManifestParam]
schema_version: Required[str]
+ """Schema version to pin to this policy set version."""
x_api_version: Annotated[str, PropertyInfo(alias="X-API-Version")]
diff --git a/src/keycardai_api/types/zones/provider.py b/src/keycardai_api/types/zones/provider.py
index 2f65195..f7d1c0a 100644
--- a/src/keycardai_api/types/zones/provider.py
+++ b/src/keycardai_api/types/zones/provider.py
@@ -65,6 +65,12 @@ class ProtocolsOauth2(BaseModel):
class ProtocolsOpenid(BaseModel):
"""OpenID Connect protocol configuration"""
+ user_identifier_claim: Optional[str] = None
+ """
+ Name of a top-level string claim in this 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
diff --git a/src/keycardai_api/types/zones/provider_create_params.py b/src/keycardai_api/types/zones/provider_create_params.py
index 4a4d37e..abb5ec2 100644
--- a/src/keycardai_api/types/zones/provider_create_params.py
+++ b/src/keycardai_api/types/zones/provider_create_params.py
@@ -93,6 +93,12 @@ class ProtocolsOauth2(TypedDict, total=False):
class ProtocolsOpenid(TypedDict, total=False):
"""OpenID Connect protocol configuration for provider creation"""
+ user_identifier_claim: str
+ """
+ Name of a top-level string claim in this 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: str
diff --git a/src/keycardai_api/types/zones/provider_update_params.py b/src/keycardai_api/types/zones/provider_update_params.py
index fe393c2..8834000 100644
--- a/src/keycardai_api/types/zones/provider_update_params.py
+++ b/src/keycardai_api/types/zones/provider_update_params.py
@@ -98,6 +98,13 @@ class ProtocolsOauth2(TypedDict, total=False):
class ProtocolsOpenid(TypedDict, total=False):
"""OpenID Connect protocol configuration. Set to null to remove all OpenID config."""
+ user_identifier_claim: Optional[str]
+ """
+ Name of a top-level string claim in this provider's ID Token to use as the user
+ identifier on user creation. Set to null to revert to default. Changing this
+ value does not affect existing users.
+ """
+
userinfo_endpoint: Optional[str]
diff --git a/src/keycardai_api/types/zones/schema_version.py b/src/keycardai_api/types/zones/schema_version.py
index f960778..3b70265 100644
--- a/src/keycardai_api/types/zones/schema_version.py
+++ b/src/keycardai_api/types/zones/schema_version.py
@@ -10,9 +10,25 @@
class SchemaVersion(BaseModel):
+ """
+ A versioned Cedar schema that defines the entity model, actions, and
+ context shape used for policy evaluation. The schema contains the valid
+ entity types (User, Application, Resource), their attributes, and the
+ allowed attribute values. See the Credentials API spec for a full
+ reference of entity attributes and valid values.
+ """
+
created_at: datetime
status: Literal["active", "deprecated", "archived"]
+ """Controls what can be done with this schema version:
+
+ - `"active"` - new policy versions can be created and validated against it.
+ - `"deprecated"` - superseded by a newer version but still accepts new policy
+ versions.
+ - `"archived"` - closed to new policy versions. Existing policy set versions
+ pinned to this schema still evaluate normally.
+ """
updated_at: datetime
diff --git a/src/keycardai_api/types/zones/schema_version_with_zone_info.py b/src/keycardai_api/types/zones/schema_version_with_zone_info.py
index a0ec53b..b50f814 100644
--- a/src/keycardai_api/types/zones/schema_version_with_zone_info.py
+++ b/src/keycardai_api/types/zones/schema_version_with_zone_info.py
@@ -6,4 +6,17 @@
class SchemaVersionWithZoneInfo(SchemaVersion):
+ """
+ A versioned Cedar schema that defines the entity model, actions, and
+ context shape used for policy evaluation. The schema contains the valid
+ entity types (User, Application, Resource), their attributes, and the
+ allowed attribute values. See the Credentials API spec for a full
+ reference of entity attributes and valid values.
+ """
+
is_default: bool
+ """Whether this is the zone's default schema.
+
+ Clients use this to pre-select which schema to write policies against. Has no
+ effect on evaluation.
+ """
diff --git a/src/keycardai_api/types/zones/user.py b/src/keycardai_api/types/zones/user.py
index 8155106..fb3cf04 100644
--- a/src/keycardai_api/types/zones/user.py
+++ b/src/keycardai_api/types/zones/user.py
@@ -23,6 +23,13 @@ class User(BaseModel):
email_verified: bool
"""Whether the email address has been verified"""
+ identifier: str
+ """Zone-scoped user identifier.
+
+ Defaults to the user's Keycard ID. When the provider has user_identifier_claim
+ configured, the value is set from that claim at user creation time.
+ """
+
organization_id: str
"""Organization that owns this user"""
diff --git a/tests/api_resources/test_organizations.py b/tests/api_resources/test_organizations.py
index efc7af8..d60ac2a 100644
--- a/tests/api_resources/test_organizations.py
+++ b/tests/api_resources/test_organizations.py
@@ -272,6 +272,7 @@ def test_method_list_identities_with_all_params(self, client: KeycardAPI) -> Non
before="x",
expand=["permissions"],
limit=1,
+ query_email="x",
role="org_admin",
x_client_request_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
)
@@ -619,6 +620,7 @@ async def test_method_list_identities_with_all_params(self, async_client: AsyncK
before="x",
expand=["permissions"],
limit=1,
+ query_email="x",
role="org_admin",
x_client_request_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
)
diff --git a/tests/api_resources/test_zones.py b/tests/api_resources/test_zones.py
index a895618..c39b354 100644
--- a/tests/api_resources/test_zones.py
+++ b/tests/api_resources/test_zones.py
@@ -12,7 +12,6 @@
from keycardai_api.types import (
Zone,
ZoneListResponse,
- ZoneListSessionResourceAccessResponse,
)
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -142,6 +141,7 @@ def test_method_update_with_all_params(self, client: KeycardAPI) -> None:
zone = client.zones.update(
zone_id="zoneId",
default_mcp_gateway_application_id="default_mcp_gateway_application_id",
+ default_resource_id="default_resource_id",
description="description",
encryption_key={
"arn": "x",
@@ -277,64 +277,6 @@ def test_path_params_delete(self, client: KeycardAPI) -> None:
"",
)
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- def test_method_list_session_resource_access(self, client: KeycardAPI) -> None:
- zone = client.zones.list_session_resource_access(
- zone_id="zoneId",
- )
- assert_matches_type(ZoneListSessionResourceAccessResponse, zone, path=["response"])
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- def test_method_list_session_resource_access_with_all_params(self, client: KeycardAPI) -> None:
- zone = client.zones.list_session_resource_access(
- zone_id="zoneId",
- after="x",
- before="x",
- expand="total_count",
- limit=1,
- resource_id="resource_id",
- rollup_children=True,
- session_id="session_id",
- user_id="user_id",
- )
- assert_matches_type(ZoneListSessionResourceAccessResponse, zone, path=["response"])
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- def test_raw_response_list_session_resource_access(self, client: KeycardAPI) -> None:
- response = client.zones.with_raw_response.list_session_resource_access(
- zone_id="zoneId",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- zone = response.parse()
- assert_matches_type(ZoneListSessionResourceAccessResponse, zone, path=["response"])
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- def test_streaming_response_list_session_resource_access(self, client: KeycardAPI) -> None:
- with client.zones.with_streaming_response.list_session_resource_access(
- zone_id="zoneId",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- zone = response.parse()
- assert_matches_type(ZoneListSessionResourceAccessResponse, zone, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- def test_path_params_list_session_resource_access(self, client: KeycardAPI) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `zone_id` but received ''"):
- client.zones.with_raw_response.list_session_resource_access(
- zone_id="",
- )
-
class TestAsyncZones:
parametrize = pytest.mark.parametrize(
@@ -462,6 +404,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncKeycardAPI
zone = await async_client.zones.update(
zone_id="zoneId",
default_mcp_gateway_application_id="default_mcp_gateway_application_id",
+ default_resource_id="default_resource_id",
description="description",
encryption_key={
"arn": "x",
@@ -596,61 +539,3 @@ async def test_path_params_delete(self, async_client: AsyncKeycardAPI) -> None:
await async_client.zones.with_raw_response.delete(
"",
)
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_method_list_session_resource_access(self, async_client: AsyncKeycardAPI) -> None:
- zone = await async_client.zones.list_session_resource_access(
- zone_id="zoneId",
- )
- assert_matches_type(ZoneListSessionResourceAccessResponse, zone, path=["response"])
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_method_list_session_resource_access_with_all_params(self, async_client: AsyncKeycardAPI) -> None:
- zone = await async_client.zones.list_session_resource_access(
- zone_id="zoneId",
- after="x",
- before="x",
- expand="total_count",
- limit=1,
- resource_id="resource_id",
- rollup_children=True,
- session_id="session_id",
- user_id="user_id",
- )
- assert_matches_type(ZoneListSessionResourceAccessResponse, zone, path=["response"])
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_raw_response_list_session_resource_access(self, async_client: AsyncKeycardAPI) -> None:
- response = await async_client.zones.with_raw_response.list_session_resource_access(
- zone_id="zoneId",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- zone = await response.parse()
- assert_matches_type(ZoneListSessionResourceAccessResponse, zone, path=["response"])
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_streaming_response_list_session_resource_access(self, async_client: AsyncKeycardAPI) -> None:
- async with async_client.zones.with_streaming_response.list_session_resource_access(
- zone_id="zoneId",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- zone = await response.parse()
- assert_matches_type(ZoneListSessionResourceAccessResponse, zone, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @pytest.mark.skip(reason="Mock server tests are disabled")
- @parametrize
- async def test_path_params_list_session_resource_access(self, async_client: AsyncKeycardAPI) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `zone_id` but received ''"):
- await async_client.zones.with_raw_response.list_session_resource_access(
- zone_id="",
- )
diff --git a/tests/api_resources/zones/test_providers.py b/tests/api_resources/zones/test_providers.py
index b4ec8d1..222ed03 100644
--- a/tests/api_resources/zones/test_providers.py
+++ b/tests/api_resources/zones/test_providers.py
@@ -57,7 +57,10 @@ def test_method_create_with_all_params(self, client: KeycardAPI) -> None:
"token_endpoint": "https://example.com",
"token_response_access_token_pointer": "token_response_access_token_pointer",
},
- "openid": {"userinfo_endpoint": "https://example.com"},
+ "openid": {
+ "user_identifier_claim": "user_identifier_claim",
+ "userinfo_endpoint": "https://example.com",
+ },
},
)
assert_matches_type(Provider, provider, path=["response"])
@@ -191,7 +194,10 @@ def test_method_update_with_all_params(self, client: KeycardAPI) -> None:
"token_endpoint": "https://example.com",
"token_response_access_token_pointer": "token_response_access_token_pointer",
},
- "openid": {"userinfo_endpoint": "https://example.com"},
+ "openid": {
+ "user_identifier_claim": "user_identifier_claim",
+ "userinfo_endpoint": "https://example.com",
+ },
},
)
assert_matches_type(Provider, provider, path=["response"])
@@ -392,7 +398,10 @@ async def test_method_create_with_all_params(self, async_client: AsyncKeycardAPI
"token_endpoint": "https://example.com",
"token_response_access_token_pointer": "token_response_access_token_pointer",
},
- "openid": {"userinfo_endpoint": "https://example.com"},
+ "openid": {
+ "user_identifier_claim": "user_identifier_claim",
+ "userinfo_endpoint": "https://example.com",
+ },
},
)
assert_matches_type(Provider, provider, path=["response"])
@@ -526,7 +535,10 @@ async def test_method_update_with_all_params(self, async_client: AsyncKeycardAPI
"token_endpoint": "https://example.com",
"token_response_access_token_pointer": "token_response_access_token_pointer",
},
- "openid": {"userinfo_endpoint": "https://example.com"},
+ "openid": {
+ "user_identifier_claim": "user_identifier_claim",
+ "userinfo_endpoint": "https://example.com",
+ },
},
)
assert_matches_type(Provider, provider, path=["response"])
diff --git a/tests/test_utils/test_path.py b/tests/test_utils/test_path.py
new file mode 100644
index 0000000..527cc60
--- /dev/null
+++ b/tests/test_utils/test_path.py
@@ -0,0 +1,89 @@
+from __future__ import annotations
+
+from typing import Any
+
+import pytest
+
+from keycardai_api._utils._path import path_template
+
+
+@pytest.mark.parametrize(
+ "template, kwargs, expected",
+ [
+ ("/v1/{id}", dict(id="abc"), "/v1/abc"),
+ ("/v1/{a}/{b}", dict(a="x", b="y"), "/v1/x/y"),
+ ("/v1/{a}{b}/path/{c}?val={d}#{e}", dict(a="x", b="y", c="z", d="u", e="v"), "/v1/xy/path/z?val=u#v"),
+ ("/{w}/{w}", dict(w="echo"), "/echo/echo"),
+ ("/v1/static", {}, "/v1/static"),
+ ("", {}, ""),
+ ("/v1/?q={n}&count=10", dict(n=42), "/v1/?q=42&count=10"),
+ ("/v1/{v}", dict(v=None), "/v1/null"),
+ ("/v1/{v}", dict(v=True), "/v1/true"),
+ ("/v1/{v}", dict(v=False), "/v1/false"),
+ ("/v1/{v}", dict(v=".hidden"), "/v1/.hidden"), # dot prefix ok
+ ("/v1/{v}", dict(v="file.txt"), "/v1/file.txt"), # dot in middle ok
+ ("/v1/{v}", dict(v="..."), "/v1/..."), # triple dot ok
+ ("/v1/{a}{b}", dict(a=".", b="txt"), "/v1/.txt"), # dot var combining with adjacent to be ok
+ ("/items?q={v}#{f}", dict(v=".", f=".."), "/items?q=.#.."), # dots in query/fragment are fine
+ (
+ "/v1/{a}?query={b}",
+ dict(a="../../other/endpoint", b="a&bad=true"),
+ "/v1/..%2F..%2Fother%2Fendpoint?query=a%26bad%3Dtrue",
+ ),
+ ("/v1/{val}", dict(val="a/b/c"), "/v1/a%2Fb%2Fc"),
+ ("/v1/{val}", dict(val="a/b/c?query=value"), "/v1/a%2Fb%2Fc%3Fquery=value"),
+ ("/v1/{val}", dict(val="a/b/c?query=value&bad=true"), "/v1/a%2Fb%2Fc%3Fquery=value&bad=true"),
+ ("/v1/{val}", dict(val="%20"), "/v1/%2520"), # escapes escape sequences in input
+ # Query: slash and ? are safe, # is not
+ ("/items?q={v}", dict(v="a/b"), "/items?q=a/b"),
+ ("/items?q={v}", dict(v="a?b"), "/items?q=a?b"),
+ ("/items?q={v}", dict(v="a#b"), "/items?q=a%23b"),
+ ("/items?q={v}", dict(v="a b"), "/items?q=a%20b"),
+ # Fragment: slash and ? are safe
+ ("/docs#{v}", dict(v="a/b"), "/docs#a/b"),
+ ("/docs#{v}", dict(v="a?b"), "/docs#a?b"),
+ # Path: slash, ? and # are all encoded
+ ("/v1/{v}", dict(v="a/b"), "/v1/a%2Fb"),
+ ("/v1/{v}", dict(v="a?b"), "/v1/a%3Fb"),
+ ("/v1/{v}", dict(v="a#b"), "/v1/a%23b"),
+ # same var encoded differently by component
+ (
+ "/v1/{v}?q={v}#{v}",
+ dict(v="a/b?c#d"),
+ "/v1/a%2Fb%3Fc%23d?q=a/b?c%23d#a/b?c%23d",
+ ),
+ ("/v1/{val}", dict(val="x?admin=true"), "/v1/x%3Fadmin=true"), # query injection
+ ("/v1/{val}", dict(val="x#admin"), "/v1/x%23admin"), # fragment injection
+ ],
+)
+def test_interpolation(template: str, kwargs: dict[str, Any], expected: str) -> None:
+ assert path_template(template, **kwargs) == expected
+
+
+def test_missing_kwarg_raises_key_error() -> None:
+ with pytest.raises(KeyError, match="org_id"):
+ path_template("/v1/{org_id}")
+
+
+@pytest.mark.parametrize(
+ "template, kwargs",
+ [
+ ("{a}/path", dict(a=".")),
+ ("{a}/path", dict(a="..")),
+ ("/v1/{a}", dict(a=".")),
+ ("/v1/{a}", dict(a="..")),
+ ("/v1/{a}/path", dict(a=".")),
+ ("/v1/{a}/path", dict(a="..")),
+ ("/v1/{a}{b}", dict(a=".", b=".")), # adjacent vars → ".."
+ ("/v1/{a}.", dict(a=".")), # var + static → ".."
+ ("/v1/{a}{b}", dict(a="", b=".")), # empty + dot → "."
+ ("/v1/%2e/{x}", dict(x="ok")), # encoded dot in static text
+ ("/v1/%2e./{x}", dict(x="ok")), # mixed encoded ".." in static
+ ("/v1/.%2E/{x}", dict(x="ok")), # mixed encoded ".." in static
+ ("/v1/{v}?q=1", dict(v="..")),
+ ("/v1/{v}#frag", dict(v="..")),
+ ],
+)
+def test_dot_segment_rejected(template: str, kwargs: dict[str, Any]) -> None:
+ with pytest.raises(ValueError, match="dot-segment"):
+ path_template(template, **kwargs)