diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 2c9fce78b18..4808d97d09e 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "5.0.0-beta.2"
+ ".": "5.1.0"
}
\ No newline at end of file
diff --git a/.stats.yml b/.stats.yml
index dfb0c0da36e..1ed9537b832 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 2195
+configured_endpoints: 2204
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare%2Fcloudflare-a6c352830d1270d0abb5bb983058ea21815e1bb7d2e163965335dcb0e706f057.yml
-openapi_spec_hash: aa452e4dfaec546a7e50ef8665bd39f4
-config_hash: d3379006654eb5479a62d9576648acc5
+openapi_spec_hash: e722ad1f58e3dfb3a928cf9890d48da4
+config_hash: ff549978909de2badc92845fea964f4b
diff --git a/CHANGELOG-v5.0.0-beta.2.md b/CHANGELOG-v5.0.0-beta.2.md
deleted file mode 100644
index 973fa0b1fba..00000000000
--- a/CHANGELOG-v5.0.0-beta.2.md
+++ /dev/null
@@ -1,177 +0,0 @@
-# v5.0.0-beta.2
-
-> This is a beta release. The API surface may change before the stable release.
-
-Full diff: [`v5.0.0-beta.1...next`](https://github.com/cloudflare/cloudflare-python/compare/v5.0.0-beta.1...next)
-
-This release adds 163 new methods across 4 new top-level resources and numerous sub-resource
-expansions, restructures the `api.md` surface into per-resource files, and includes codegen
-updates across all existing resources.
-
-## General Changes
-
-### Client-level `account_id` and `zone_id` configuration
-
-You can now set `account_id` and `zone_id` once on the client instead of passing them to every
-method call. When set, these values are used as defaults for all methods that accept them. You
-can still override per-call by passing the parameter explicitly.
-
-```python
-from cloudflare import Cloudflare
-
-# Set account_id once at the client level
-client = Cloudflare(account_id="my-account-id")
-
-# No need to pass account_id on every call
-workers = client.workers.scripts.list()
-rules = client.rules.lists.list()
-```
-
-Both options also read from environment variables:
-
-- `account_id` from `CLOUDFLARE_ACCOUNT_ID`
-- `zone_id` from `CLOUDFLARE_ZONE_ID`
-
-```python
-# Or configure via environment variables
-# export CLOUDFLARE_ACCOUNT_ID=my-account-id
-# export CLOUDFLARE_ZONE_ID=my-zone-id
-client = Cloudflare()
-```
-
-All methods that previously required `account_id` or `zone_id` as a positional/keyword argument
-now accept `None` as a default and fall back to the client-level value. If neither is set, a
-`ValueError` is raised with a clear message.
-
-## Breaking Changes
-
-### Restructured Resources
-
-- **AISearch**: `aisearch.instances.items` (`get`, `list`) has been replaced by the new
- namespace-based architecture under `aisearch.namespaces.instances.items` (see New Resources
- below)
-
-- **Registrar**: `registrar.domains` (`get`, `list`, `update`) has been replaced by
- `registrar.registrations` with expanded functionality including `create`, `edit`, `get`, `list`,
- plus new `registrar.check`, `registrar.search`, `registrar.registration_status.get`, and
- `registrar.update_status.get` methods
-
-- **Custom Pages**: `custom_pages` (`get`, `list`, `update`) has been replaced by
- `custom_pages.assets` (`create`, `delete`, `get`, `list`, `update`)
-
-### Removed Methods
-
-- `cloudforce_one.threat_events.delete`
-
-## New API Resources
-
-### Top-Level Resources
-
-- **VulnerabilityScanner** -- Full CRUD for vulnerability scanning
- - `credential_sets` -- `create`, `delete`, `edit`, `get`, `list`, `update`
- - `credential_sets.credentials` -- `create`, `delete`, `edit`, `get`, `list`, `update`
- - `scans` -- `create`, `get`, `list`
- - `target_environments` -- `create`, `delete`, `edit`, `get`, `list`, `update`
-
-- **GoogleTagGateway** -- Google Tag Gateway configuration
- - `config` -- `get`, `update`
-
-- **EmailSending** -- Email sending with subdomain management
- - `send`, `send_raw`
- - `subdomains` -- `create`, `delete`, `get`, `list`
- - `subdomains.dns` -- `get`
-
-- **ResourceTagging** -- Resource tagging and key/value management
- - `list`
- - `account_tags` -- `delete`, `get`, `update`
- - `zone_tags` -- `delete`, `get`, `update`
- - `keys` -- `list`
- - `values` -- `list`
-
-### New Sub-Resources on Existing Resources
-
-- **AISearch** (restructured) -- Expanded namespace-based architecture
- - `aisearch.namespaces` -- `create`, `delete`, `list`, `read`, `search`, `update`,
- `chat_completions`
- - `aisearch.namespaces.instances` -- `create`, `list`, `read`, `search`, `stats`, `update`,
- `chat_completions`
- - `aisearch.namespaces.instances.items` -- `chunks`, `create_or_update`, `delete`, `download`,
- `get`, `list`, `logs`, `sync`, `upload`
- - `aisearch.namespaces.instances.jobs` -- `create`, `get`, `list`, `logs`, `update`
-
-- **APIGateway Labels** -- Label management for API Gateway operations
- - `api_gateway.labels` -- `list`
- - `api_gateway.labels.managed` -- `get`
- - `api_gateway.labels.managed.resources.operation` -- `update`
- - `api_gateway.labels.user` -- `bulk_create`, `bulk_delete`, `delete`, `edit`, `get`, `update`
- - `api_gateway.labels.user.resources.operation` -- `update`
- - `api_gateway.operations.labels` -- `bulk_create`, `bulk_delete`, `bulk_update`, `create`,
- `delete`, `update`
-
-- **BrowserRendering** -- DevTools and crawl APIs
- - `browser_rendering.crawl` -- `create`, `delete`, `get`
- - `browser_rendering.devtools.browser` -- `connect`, `create`, `delete`, `launch`, `protocol`,
- `version`
- - `browser_rendering.devtools.browser.page` -- `get`
- - `browser_rendering.devtools.browser.targets` -- `activate`, `create`, `get`, `list`
- - `browser_rendering.devtools.session` -- `get`, `list`
-
-- **Custom Pages Assets** -- Custom page asset management
- - `custom_pages.assets` -- `create`, `delete`, `get`, `list`, `update`
-
-- **ACM Custom Trust Store** -- Custom origin trust store
- - `acm.custom_trust_store` -- `create`, `delete`, `get`, `list`
-
-- **Workers Observability Destinations** -- Log destinations for Workers observability
- - `workers.observability.destinations` -- `create`, `delete`, `list`, `update`
-
-- **Registrar** (expanded) -- Domain registration with full lifecycle management
- - `registrar.registrations` -- `create`, `edit`, `get`, `list`
- - `registrar.check`, `registrar.search`
- - `registrar.registration_status` -- `get`
- - `registrar.update_status` -- `get`
-
-- **Zero Trust** additions
- - `zero_trust.access.users` -- `create`, `delete`, `get`, `update`
- - `zero_trust.devices.ip_profiles` -- `create`, `delete`, `get`, `list`, `update`
- - `zero_trust.dex.rules` -- `create`, `delete`, `get`, `list`, `update`
- - `zero_trust.dlp.settings` -- `delete`, `edit`, `get`, `update`
- - `zero_trust.gateway.pacfiles` -- `create`, `delete`, `get`, `list`, `update`
- - `zero_trust.networks.subnets.warp` -- `create`, `delete`, `edit`, `get`
- - `zero_trust.tunnels.warp_connector.connections` -- `get`
- - `zero_trust.tunnels.warp_connector.connectors` -- `get`
- - `zero_trust.tunnels.warp_connector.failover` -- `update`
-
-- **Zones Environments** -- Zone environment management
- - `zones.environments` -- `create`, `delete`, `edit`, `list`, `rollback`, `update`
-
-- **Radar** additions
- - `radar.agent_readiness` -- `summary`
- - `radar.ai.markdown_for_agents` -- `summary`, `timeseries`
- - `radar.entities.asns` -- `botnet_threat_feed`
- - `radar.post_quantum.origin` -- `summary`, `timeseries_groups`
- - `radar.post_quantum.tls` -- `support`
-
-- **Billing Usage** -- PayGo billing usage
- - `billing.usage` -- `paygo`
-
-- **Email Security** -- PhishGuard reporting
- - `email_security.phishguard.reports` -- `list`
-
-- **Brand Protection** -- v2 endpoints
- - `brand_protection.v2` additions
-
-## Bug Fixes
-
-- **fix(dlp)**: Add missing `model_rebuild()`/`update_forward_refs()` calls for
- `CustomProfileSharedEntryCustomEntry` and `IntegrationProfileSharedEntryCustomEntry`, fixing
- pydantic v1 validation errors on DLP profile types.
-- **fix(workers)**: Make `RunQueryParametersNeedleValue` a `BaseModel` with
- `arbitrary_types_allowed` to prevent pydantic config errors.
-
-## Internal
-
-- `api.md` has been restructured from a single monolithic file into per-resource files under
- `src/cloudflare/resources/*/api.md`. The root `api.md` now contains shared types and links to
- each resource's documentation.
-- Codegen updates applied across all resources with updated type annotations and method signatures.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 76f06d9abbc..eafbf0400f9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,63 @@
# Changelog
+## 5.1.0 (2026-05-06)
+
+Full Changelog: [v5.0.0-beta.2...v5.1.0](https://github.com/cloudflare/cloudflare-python/compare/v5.0.0-beta.2...v5.1.0)
+
+### Features
+
+* **api:** api update ([3b83baa](https://github.com/cloudflare/cloudflare-python/commit/3b83baa3be725513c98c33e678886f83f5c6b2ce))
+* chore: remove account_id and zone_id client options ([988df86](https://github.com/cloudflare/cloudflare-python/commit/988df8632b0b531c5b1117ecd7ed7d77925dd5ac))
+* feat: add organization audit logs endpoint ([6810580](https://github.com/cloudflare/cloudflare-python/commit/681058048aedeb0983d182b1168b131c332e1477))
+* feat(cache): Add origin cloud regions resource to docs/sdk for new API ([c7b5b51](https://github.com/cloudflare/cloudflare-python/commit/c7b5b51764e7c343140d91d4fd86cf7275eec271))
+* feat(iam): add user_groups and user_group_members terraform resources ([cb00d08](https://github.com/cloudflare/cloudflare-python/commit/cb00d08d86197bddbd55bd3107473c1e363c12c4))
+* **security_center:** update generated types and methods ([ed7d261](https://github.com/cloudflare/cloudflare-python/commit/ed7d261e696fd629101ec2c9197597287d12f479))
+* **zero_trust:** update generated types and methods ([7121a55](https://github.com/cloudflare/cloudflare-python/commit/7121a55d36569da5f7058a749952032c66687d67))
+
+
+### Bug Fixes
+
+* **_models:** add polymorphic_serialization parameter to model_dump overrides ([342b5a8](https://github.com/cloudflare/cloudflare-python/commit/342b5a84daaf5c09e3e1612809d956d916a43621))
+* **ci:** pin single Python version to match pre-uv test behavior ([86676bc](https://github.com/cloudflare/cloudflare-python/commit/86676bca670d38d5947041bc5677138640dd6d92))
+* **ci:** run tests with single Python version and pydantic v2 only ([3259115](https://github.com/cloudflare/cloudflare-python/commit/3259115d9c1c5e2f116adda878851afc86ce5239))
+* **pipelines:** add BaseModel base to response SchemaFieldStruct/SchemaFieldList stubs ([d33af8b](https://github.com/cloudflare/cloudflare-python/commit/d33af8b5d5c1705fde4ae73efdbe3bfe3ba321df))
+* **radar:** move type: ignore[call-arg] to call site for mypy ([fac9404](https://github.com/cloudflare/cloudflare-python/commit/fac9404ebfa1eb5383a3d3c54f49e4120eb8cd11))
+* resolve pre-existing codegen type errors ([fed88d6](https://github.com/cloudflare/cloudflare-python/commit/fed88d6c5a5e482a9de595a1431915320fa11d23))
+
+
+### Chores
+
+* **accounts:** update generated types ([7ff4adf](https://github.com/cloudflare/cloudflare-python/commit/7ff4adf1cf53aae127ffcf059dbfd60620c661e5))
+* **aisearch:** update generated types and methods ([54b8775](https://github.com/cloudflare/cloudflare-python/commit/54b87759b6046cbd0e3330f3e32c80170fe9de61))
+* **aisearch:** update generated types and methods ([f22897e](https://github.com/cloudflare/cloudflare-python/commit/f22897e1ec7ac175278e5481c4855b44fe093a9d))
+* **aisearch:** update generated types, remove unused tests ([e20c138](https://github.com/cloudflare/cloudflare-python/commit/e20c1386bec9181c3dd6dae42a6dee58fc62f1fb))
+* **api:** update composite API spec ([511b508](https://github.com/cloudflare/cloudflare-python/commit/511b508a3dcdbce558c32bee20a2d2e2edefa689))
+* **api:** update composite API spec ([0ee5719](https://github.com/cloudflare/cloudflare-python/commit/0ee571927b0b9656256ed22d76a412b262241f44))
+* **api:** update composite API spec ([6bf05ea](https://github.com/cloudflare/cloudflare-python/commit/6bf05ea320bcbdf1d9fd30edc6d9a19a626a7d22))
+* **api:** update composite API spec ([fd0fca0](https://github.com/cloudflare/cloudflare-python/commit/fd0fca0667e9ca3837629d1aeb1ede7134f0253d))
+* **api:** update composite API spec ([67db9e9](https://github.com/cloudflare/cloudflare-python/commit/67db9e976b1f31e0a4526c76b8149390be128bd8))
+* **api:** update composite API spec ([0fd3baf](https://github.com/cloudflare/cloudflare-python/commit/0fd3baf76be724534d7693eb5a2c3da1b5144c57))
+* **api:** update composite API spec ([42c4dbf](https://github.com/cloudflare/cloudflare-python/commit/42c4dbfa49e879c59dd5e1100c9d5c0191c968d2))
+* **api:** upload stainless config from cloudflare-config ([0d64642](https://github.com/cloudflare/cloudflare-python/commit/0d646425898be57e654301e98c37beeb69d541c4))
+* apply shared infrastructure changes (rye -> uv migration) ([2f283c2](https://github.com/cloudflare/cloudflare-python/commit/2f283c29584b49e7c000f5e04aa5edc62db36ab9))
+* delete changelog draft ([f941e66](https://github.com/cloudflare/cloudflare-python/commit/f941e66bec7889ab45cd373be4209d7e9d5de64d))
+* **email_security:** remove deprecated type definitions ([878e3d4](https://github.com/cloudflare/cloudflare-python/commit/878e3d461cc5704a76665cb47e73b0f153f10546))
+* **email_security:** update generated types and methods ([23a979d](https://github.com/cloudflare/cloudflare-python/commit/23a979df28a8e2dedbf0ea83b7f6bfa69e48c61b))
+* **fraud:** update generated types and methods ([771c7e4](https://github.com/cloudflare/cloudflare-python/commit/771c7e45ce91554f4897bdc8cdea0ceedc2c48ab))
+* **internal:** version bump ([0a576fa](https://github.com/cloudflare/cloudflare-python/commit/0a576fa0cbe15e8ebee4ff4ca489d8362885231e))
+* **r2:** update generated types ([2e7856d](https://github.com/cloudflare/cloudflare-python/commit/2e7856d879a82674f6f141c6eef33576694cca0a))
+* **radar:** update generated types and methods ([b48274e](https://github.com/cloudflare/cloudflare-python/commit/b48274ef74a2ac8f58444cbbeb220ba1a425b5b9))
+* **radar:** update to_markdown type ignore comments ([d721964](https://github.com/cloudflare/cloudflare-python/commit/d721964e85978708190d31ba12a15b05f840c0b7))
+* sync shared codegen files from staging-next ([b8078b3](https://github.com/cloudflare/cloudflare-python/commit/b8078b3ac389f386263fb3d5c9d8c6a3b7db6be0))
+* sync shared codegen files from staging-next ([2802feb](https://github.com/cloudflare/cloudflare-python/commit/2802feb0090eb951a2393b7677b2601ed279d85f))
+* **workers_for_platforms:** update generated types ([f342310](https://github.com/cloudflare/cloudflare-python/commit/f3423103c48ad1308775105be71e2243ab1865ed))
+* **zones:** update generated types and methods ([f0a5409](https://github.com/cloudflare/cloudflare-python/commit/f0a540997c0a39a2a86227b71836542db258cc18))
+
+
+### Documentation
+
+* add Authentication section to README ([70d934a](https://github.com/cloudflare/cloudflare-python/commit/70d934abd91cfa8f8444e0651ac97012c1d0edcf))
+
## 5.0.0-beta.2 (2026-04-20)
Full Changelog: [v5.0.0-beta.1...v5.0.0-beta.2](https://github.com/cloudflare/cloudflare-python/compare/v5.0.0-beta.1...v5.0.0-beta.2)
diff --git a/README.md b/README.md
index 6b81dde75a4..f5c382875fc 100644
--- a/README.md
+++ b/README.md
@@ -26,7 +26,7 @@ The REST API documentation can be found on [developers.cloudflare.com](https://d
```sh
# install from PyPI
-pip install --pre cloudflare
+pip install cloudflare
```
## Usage
@@ -90,7 +90,7 @@ You can enable this by installing `aiohttp`:
```sh
# install from PyPI
-pip install --pre cloudflare[aiohttp]
+pip install cloudflare[aiohttp]
```
Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`:
diff --git a/pyproject.toml b/pyproject.toml
index a79b693633d..a238c1c36c9 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "cloudflare"
-version = "5.0.0-beta.2"
+version = "5.1.0"
description = "The official Python library for the cloudflare API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/cloudflare/_version.py b/src/cloudflare/_version.py
index f204e83c6ac..a5c75b18f6d 100644
--- a/src/cloudflare/_version.py
+++ b/src/cloudflare/_version.py
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
__title__ = "cloudflare"
-__version__ = "5.0.0-beta.2" # x-release-please-version
+__version__ = "5.1.0" # x-release-please-version
diff --git a/src/cloudflare/resources/aisearch/instances/instances.py b/src/cloudflare/resources/aisearch/instances/instances.py
index 0b4aa0f18c3..58da83de22e 100644
--- a/src/cloudflare/resources/aisearch/instances/instances.py
+++ b/src/cloudflare/resources/aisearch/instances/instances.py
@@ -115,6 +115,7 @@ def create(
cache: bool | Omit = omit,
cache_threshold: Literal["super_strict_match", "close_enough", "flexible_friend", "anything_goes"]
| Omit = omit,
+ cache_ttl: Literal[600, 1800, 3600, 7200, 21600, 43200, 86400, 172800, 259200, 518400] | Omit = omit,
chunk: bool | Omit = omit,
chunk_overlap: int | Omit = omit,
chunk_size: int | Omit = omit,
@@ -199,6 +200,10 @@ def create(
Lowercase alphanumeric, hyphens, and underscores.
+ cache_ttl: Cache entry TTL in seconds. Allowed values: 600 (10min), 1800 (30min), 3600
+ (1h), 7200 (2h), 21600 (6h), 43200 (12h), 86400 (24h), 172800 (48h), 259200
+ (72h), 518400 (6d).
+
hybrid_search_enabled: Deprecated — use index_method instead.
index_method: Controls which storage backends are used during indexing. Defaults to
@@ -226,6 +231,7 @@ def create(
"aisearch_model": aisearch_model,
"cache": cache,
"cache_threshold": cache_threshold,
+ "cache_ttl": cache_ttl,
"chunk": chunk,
"chunk_overlap": chunk_overlap,
"chunk_size": chunk_size,
@@ -306,6 +312,7 @@ def update(
cache: bool | Omit = omit,
cache_threshold: Literal["super_strict_match", "close_enough", "flexible_friend", "anything_goes"]
| Omit = omit,
+ cache_ttl: Literal[600, 1800, 3600, 7200, 21600, 43200, 86400, 172800, 259200, 518400] | Omit = omit,
chunk: bool | Omit = omit,
chunk_overlap: int | Omit = omit,
chunk_size: int | Omit = omit,
@@ -420,10 +427,15 @@ def update(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> InstanceUpdateResponse:
- """
- Update instance.
+ """Update instance.
Args:
+ cache_ttl: Cache entry TTL in seconds.
+
+ Allowed values: 600 (10min), 1800 (30min), 3600
+ (1h), 7200 (2h), 21600 (6h), 43200 (12h), 86400 (24h), 172800 (48h), 259200
+ (72h), 518400 (6d).
+
index_method: Controls which storage backends are used during indexing. Defaults to
vector-only.
@@ -450,6 +462,7 @@ def update(
"aisearch_model": aisearch_model,
"cache": cache,
"cache_threshold": cache_threshold,
+ "cache_ttl": cache_ttl,
"chunk": chunk,
"chunk_overlap": chunk_overlap,
"chunk_size": chunk_size,
@@ -889,6 +902,7 @@ async def create(
cache: bool | Omit = omit,
cache_threshold: Literal["super_strict_match", "close_enough", "flexible_friend", "anything_goes"]
| Omit = omit,
+ cache_ttl: Literal[600, 1800, 3600, 7200, 21600, 43200, 86400, 172800, 259200, 518400] | Omit = omit,
chunk: bool | Omit = omit,
chunk_overlap: int | Omit = omit,
chunk_size: int | Omit = omit,
@@ -973,6 +987,10 @@ async def create(
Lowercase alphanumeric, hyphens, and underscores.
+ cache_ttl: Cache entry TTL in seconds. Allowed values: 600 (10min), 1800 (30min), 3600
+ (1h), 7200 (2h), 21600 (6h), 43200 (12h), 86400 (24h), 172800 (48h), 259200
+ (72h), 518400 (6d).
+
hybrid_search_enabled: Deprecated — use index_method instead.
index_method: Controls which storage backends are used during indexing. Defaults to
@@ -1000,6 +1018,7 @@ async def create(
"aisearch_model": aisearch_model,
"cache": cache,
"cache_threshold": cache_threshold,
+ "cache_ttl": cache_ttl,
"chunk": chunk,
"chunk_overlap": chunk_overlap,
"chunk_size": chunk_size,
@@ -1080,6 +1099,7 @@ async def update(
cache: bool | Omit = omit,
cache_threshold: Literal["super_strict_match", "close_enough", "flexible_friend", "anything_goes"]
| Omit = omit,
+ cache_ttl: Literal[600, 1800, 3600, 7200, 21600, 43200, 86400, 172800, 259200, 518400] | Omit = omit,
chunk: bool | Omit = omit,
chunk_overlap: int | Omit = omit,
chunk_size: int | Omit = omit,
@@ -1194,10 +1214,15 @@ async def update(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> InstanceUpdateResponse:
- """
- Update instance.
+ """Update instance.
Args:
+ cache_ttl: Cache entry TTL in seconds.
+
+ Allowed values: 600 (10min), 1800 (30min), 3600
+ (1h), 7200 (2h), 21600 (6h), 43200 (12h), 86400 (24h), 172800 (48h), 259200
+ (72h), 518400 (6d).
+
index_method: Controls which storage backends are used during indexing. Defaults to
vector-only.
@@ -1224,6 +1249,7 @@ async def update(
"aisearch_model": aisearch_model,
"cache": cache,
"cache_threshold": cache_threshold,
+ "cache_ttl": cache_ttl,
"chunk": chunk,
"chunk_overlap": chunk_overlap,
"chunk_size": chunk_size,
diff --git a/src/cloudflare/resources/aisearch/namespaces/instances/instances.py b/src/cloudflare/resources/aisearch/namespaces/instances/instances.py
index d13a23fec7b..c0859761db9 100644
--- a/src/cloudflare/resources/aisearch/namespaces/instances/instances.py
+++ b/src/cloudflare/resources/aisearch/namespaces/instances/instances.py
@@ -128,6 +128,7 @@ def create(
cache: bool | Omit = omit,
cache_threshold: Literal["super_strict_match", "close_enough", "flexible_friend", "anything_goes"]
| Omit = omit,
+ cache_ttl: Literal[600, 1800, 3600, 7200, 21600, 43200, 86400, 172800, 259200, 518400] | Omit = omit,
chunk: bool | Omit = omit,
chunk_overlap: int | Omit = omit,
chunk_size: int | Omit = omit,
@@ -212,6 +213,10 @@ def create(
Lowercase alphanumeric, hyphens, and underscores.
+ cache_ttl: Cache entry TTL in seconds. Allowed values: 600 (10min), 1800 (30min), 3600
+ (1h), 7200 (2h), 21600 (6h), 43200 (12h), 86400 (24h), 172800 (48h), 259200
+ (72h), 518400 (6d).
+
hybrid_search_enabled: Deprecated — use index_method instead.
index_method: Controls which storage backends are used during indexing. Defaults to
@@ -243,6 +248,7 @@ def create(
"aisearch_model": aisearch_model,
"cache": cache,
"cache_threshold": cache_threshold,
+ "cache_ttl": cache_ttl,
"chunk": chunk,
"chunk_overlap": chunk_overlap,
"chunk_size": chunk_size,
@@ -324,6 +330,7 @@ def update(
cache: bool | Omit = omit,
cache_threshold: Literal["super_strict_match", "close_enough", "flexible_friend", "anything_goes"]
| Omit = omit,
+ cache_ttl: Literal[600, 1800, 3600, 7200, 21600, 43200, 86400, 172800, 259200, 518400] | Omit = omit,
chunk: bool | Omit = omit,
chunk_overlap: int | Omit = omit,
chunk_size: int | Omit = omit,
@@ -438,10 +445,15 @@ def update(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> InstanceUpdateResponse:
- """
- Update instance.
+ """Update instance.
Args:
+ cache_ttl: Cache entry TTL in seconds.
+
+ Allowed values: 600 (10min), 1800 (30min), 3600
+ (1h), 7200 (2h), 21600 (6h), 43200 (12h), 86400 (24h), 172800 (48h), 259200
+ (72h), 518400 (6d).
+
index_method: Controls which storage backends are used during indexing. Defaults to
vector-only.
@@ -475,6 +487,7 @@ def update(
"aisearch_model": aisearch_model,
"cache": cache,
"cache_threshold": cache_threshold,
+ "cache_ttl": cache_ttl,
"chunk": chunk,
"chunk_overlap": chunk_overlap,
"chunk_size": chunk_size,
@@ -962,6 +975,7 @@ async def create(
cache: bool | Omit = omit,
cache_threshold: Literal["super_strict_match", "close_enough", "flexible_friend", "anything_goes"]
| Omit = omit,
+ cache_ttl: Literal[600, 1800, 3600, 7200, 21600, 43200, 86400, 172800, 259200, 518400] | Omit = omit,
chunk: bool | Omit = omit,
chunk_overlap: int | Omit = omit,
chunk_size: int | Omit = omit,
@@ -1046,6 +1060,10 @@ async def create(
Lowercase alphanumeric, hyphens, and underscores.
+ cache_ttl: Cache entry TTL in seconds. Allowed values: 600 (10min), 1800 (30min), 3600
+ (1h), 7200 (2h), 21600 (6h), 43200 (12h), 86400 (24h), 172800 (48h), 259200
+ (72h), 518400 (6d).
+
hybrid_search_enabled: Deprecated — use index_method instead.
index_method: Controls which storage backends are used during indexing. Defaults to
@@ -1077,6 +1095,7 @@ async def create(
"aisearch_model": aisearch_model,
"cache": cache,
"cache_threshold": cache_threshold,
+ "cache_ttl": cache_ttl,
"chunk": chunk,
"chunk_overlap": chunk_overlap,
"chunk_size": chunk_size,
@@ -1158,6 +1177,7 @@ async def update(
cache: bool | Omit = omit,
cache_threshold: Literal["super_strict_match", "close_enough", "flexible_friend", "anything_goes"]
| Omit = omit,
+ cache_ttl: Literal[600, 1800, 3600, 7200, 21600, 43200, 86400, 172800, 259200, 518400] | Omit = omit,
chunk: bool | Omit = omit,
chunk_overlap: int | Omit = omit,
chunk_size: int | Omit = omit,
@@ -1272,10 +1292,15 @@ async def update(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> InstanceUpdateResponse:
- """
- Update instance.
+ """Update instance.
Args:
+ cache_ttl: Cache entry TTL in seconds.
+
+ Allowed values: 600 (10min), 1800 (30min), 3600
+ (1h), 7200 (2h), 21600 (6h), 43200 (12h), 86400 (24h), 172800 (48h), 259200
+ (72h), 518400 (6d).
+
index_method: Controls which storage backends are used during indexing. Defaults to
vector-only.
@@ -1309,6 +1334,7 @@ async def update(
"aisearch_model": aisearch_model,
"cache": cache,
"cache_threshold": cache_threshold,
+ "cache_ttl": cache_ttl,
"chunk": chunk,
"chunk_overlap": chunk_overlap,
"chunk_size": chunk_size,
diff --git a/src/cloudflare/resources/email_security/submissions.py b/src/cloudflare/resources/email_security/submissions.py
index be830a3f4e9..55221b2c05e 100644
--- a/src/cloudflare/resources/email_security/submissions.py
+++ b/src/cloudflare/resources/email_security/submissions.py
@@ -51,6 +51,7 @@ def list(
*,
account_id: str,
end: Union[str, datetime] | Omit = omit,
+ escalated_from_user: bool | Omit = omit,
original_disposition: Literal["MALICIOUS", "SUSPICIOUS", "SPOOF", "SPAM", "BULK", "NONE"] | Omit = omit,
outcome_disposition: Literal["MALICIOUS", "SUSPICIOUS", "SPOOF", "SPAM", "BULK", "NONE"] | Omit = omit,
page: int | Omit = omit,
@@ -79,6 +80,10 @@ def list(
end: The end of the search date range. Defaults to `now`.
+ escalated_from_user: When true, return only submissions that were escalated by an end user (vs. by
+ the security team). When false, return only submissions that were not escalated
+ by an end user. When omitted, no filter is applied.
+
page: Current page within paginated list of results.
per_page: The number of results per page. Maximum value is 1000.
@@ -106,6 +111,7 @@ def list(
query=maybe_transform(
{
"end": end,
+ "escalated_from_user": escalated_from_user,
"original_disposition": original_disposition,
"outcome_disposition": outcome_disposition,
"page": page,
@@ -149,6 +155,7 @@ def list(
*,
account_id: str,
end: Union[str, datetime] | Omit = omit,
+ escalated_from_user: bool | Omit = omit,
original_disposition: Literal["MALICIOUS", "SUSPICIOUS", "SPOOF", "SPAM", "BULK", "NONE"] | Omit = omit,
outcome_disposition: Literal["MALICIOUS", "SUSPICIOUS", "SPOOF", "SPAM", "BULK", "NONE"] | Omit = omit,
page: int | Omit = omit,
@@ -177,6 +184,10 @@ def list(
end: The end of the search date range. Defaults to `now`.
+ escalated_from_user: When true, return only submissions that were escalated by an end user (vs. by
+ the security team). When false, return only submissions that were not escalated
+ by an end user. When omitted, no filter is applied.
+
page: Current page within paginated list of results.
per_page: The number of results per page. Maximum value is 1000.
@@ -204,6 +215,7 @@ def list(
query=maybe_transform(
{
"end": end,
+ "escalated_from_user": escalated_from_user,
"original_disposition": original_disposition,
"outcome_disposition": outcome_disposition,
"page": page,
diff --git a/src/cloudflare/resources/radar/dns/dns.py b/src/cloudflare/resources/radar/dns/dns.py
index d5dd5c4a307..59c30541a27 100644
--- a/src/cloudflare/resources/radar/dns/dns.py
+++ b/src/cloudflare/resources/radar/dns/dns.py
@@ -628,6 +628,7 @@ def timeseries_groups_v2(
"RESPONSE_CODE",
"RESPONSE_TTL",
"TLD",
+ "TLD_DNS_MAGNITUDE",
],
*,
agg_interval: Literal["15m", "1h", "1d", "1w"] | Omit = omit,
@@ -647,7 +648,7 @@ def timeseries_groups_v2(
matching_answer: Iterable[bool] | Omit = omit,
name: SequenceNotStr[str] | Omit = omit,
nodata: Iterable[bool] | Omit = omit,
- normalization: Literal["PERCENTAGE", "MIN0_MAX"] | Omit = omit,
+ normalization: Literal["PERCENTAGE", "MIN0_MAX", "RANK"] | Omit = omit,
protocol: List[Literal["UDP", "TCP", "HTTPS", "TLS"]] | Omit = omit,
query_type: List[
Optional[
@@ -1473,6 +1474,7 @@ async def timeseries_groups_v2(
"RESPONSE_CODE",
"RESPONSE_TTL",
"TLD",
+ "TLD_DNS_MAGNITUDE",
],
*,
agg_interval: Literal["15m", "1h", "1d", "1w"] | Omit = omit,
@@ -1492,7 +1494,7 @@ async def timeseries_groups_v2(
matching_answer: Iterable[bool] | Omit = omit,
name: SequenceNotStr[str] | Omit = omit,
nodata: Iterable[bool] | Omit = omit,
- normalization: Literal["PERCENTAGE", "MIN0_MAX"] | Omit = omit,
+ normalization: Literal["PERCENTAGE", "MIN0_MAX", "RANK"] | Omit = omit,
protocol: List[Literal["UDP", "TCP", "HTTPS", "TLS"]] | Omit = omit,
query_type: List[
Optional[
diff --git a/src/cloudflare/resources/security_center/api.md b/src/cloudflare/resources/security_center/api.md
index 5ba93b613e2..2fea22c8f78 100644
--- a/src/cloudflare/resources/security_center/api.md
+++ b/src/cloudflare/resources/security_center/api.md
@@ -48,3 +48,43 @@ from cloudflare.types.security_center.insights import TypeGetResponse
Methods:
- client.security_center.insights.type.get(\*, account_id, zone_id, \*\*params) -> Optional[TypeGetResponse]
+
+### AuditLogs
+
+Types:
+
+```python
+from cloudflare.types.security_center.insights import (
+ AuditLogListResponse,
+ AuditLogListByInsightResponse,
+)
+```
+
+Methods:
+
+- client.security_center.insights.audit_logs.list(\*, account_id, zone_id, \*\*params) -> SyncCursorPagination[AuditLogListResponse]
+- client.security_center.insights.audit_logs.list_by_insight(issue_id, \*, account_id, zone_id, \*\*params) -> SyncCursorPagination[AuditLogListByInsightResponse]
+
+### Classification
+
+Types:
+
+```python
+from cloudflare.types.security_center.insights import ClassificationUpdateResponse
+```
+
+Methods:
+
+- client.security_center.insights.classification.update(issue_id, \*, account_id, zone_id, \*\*params) -> ClassificationUpdateResponse
+
+### Context
+
+Types:
+
+```python
+from cloudflare.types.security_center.insights import ContextGetResponse
+```
+
+Methods:
+
+- client.security_center.insights.context.get(issue_id, \*, account_id) -> Optional[ContextGetResponse]
diff --git a/src/cloudflare/resources/security_center/insights/__init__.py b/src/cloudflare/resources/security_center/insights/__init__.py
index 5818ca6cde6..7048fdf8957 100644
--- a/src/cloudflare/resources/security_center/insights/__init__.py
+++ b/src/cloudflare/resources/security_center/insights/__init__.py
@@ -16,6 +16,14 @@
ClassResourceWithStreamingResponse,
AsyncClassResourceWithStreamingResponse,
)
+from .context import (
+ ContextResource,
+ AsyncContextResource,
+ ContextResourceWithRawResponse,
+ AsyncContextResourceWithRawResponse,
+ ContextResourceWithStreamingResponse,
+ AsyncContextResourceWithStreamingResponse,
+)
from .insights import (
InsightsResource,
AsyncInsightsResource,
@@ -32,6 +40,22 @@
SeverityResourceWithStreamingResponse,
AsyncSeverityResourceWithStreamingResponse,
)
+from .audit_logs import (
+ AuditLogsResource,
+ AsyncAuditLogsResource,
+ AuditLogsResourceWithRawResponse,
+ AsyncAuditLogsResourceWithRawResponse,
+ AuditLogsResourceWithStreamingResponse,
+ AsyncAuditLogsResourceWithStreamingResponse,
+)
+from .classification import (
+ ClassificationResource,
+ AsyncClassificationResource,
+ ClassificationResourceWithRawResponse,
+ AsyncClassificationResourceWithRawResponse,
+ ClassificationResourceWithStreamingResponse,
+ AsyncClassificationResourceWithStreamingResponse,
+)
__all__ = [
"ClassResource",
@@ -52,6 +76,24 @@
"AsyncTypeResourceWithRawResponse",
"TypeResourceWithStreamingResponse",
"AsyncTypeResourceWithStreamingResponse",
+ "AuditLogsResource",
+ "AsyncAuditLogsResource",
+ "AuditLogsResourceWithRawResponse",
+ "AsyncAuditLogsResourceWithRawResponse",
+ "AuditLogsResourceWithStreamingResponse",
+ "AsyncAuditLogsResourceWithStreamingResponse",
+ "ClassificationResource",
+ "AsyncClassificationResource",
+ "ClassificationResourceWithRawResponse",
+ "AsyncClassificationResourceWithRawResponse",
+ "ClassificationResourceWithStreamingResponse",
+ "AsyncClassificationResourceWithStreamingResponse",
+ "ContextResource",
+ "AsyncContextResource",
+ "ContextResourceWithRawResponse",
+ "AsyncContextResourceWithRawResponse",
+ "ContextResourceWithStreamingResponse",
+ "AsyncContextResourceWithStreamingResponse",
"InsightsResource",
"AsyncInsightsResource",
"InsightsResourceWithRawResponse",
diff --git a/src/cloudflare/resources/security_center/insights/audit_logs.py b/src/cloudflare/resources/security_center/insights/audit_logs.py
new file mode 100644
index 00000000000..aeb240a3f62
--- /dev/null
+++ b/src/cloudflare/resources/security_center/insights/audit_logs.py
@@ -0,0 +1,489 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union
+from datetime import datetime
+from typing_extensions import Literal
+
+import httpx
+
+from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ...._utils import path_template, maybe_transform
+from ...._compat import cached_property
+from ...._resource import SyncAPIResource, AsyncAPIResource
+from ...._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ....pagination import SyncCursorPagination, AsyncCursorPagination
+from ...._base_client import AsyncPaginator, make_request_options
+from ....types.security_center.insights import audit_log_list_params, audit_log_list_by_insight_params
+from ....types.security_center.insights.audit_log_list_response import AuditLogListResponse
+from ....types.security_center.insights.audit_log_list_by_insight_response import AuditLogListByInsightResponse
+
+__all__ = ["AuditLogsResource", "AsyncAuditLogsResource"]
+
+
+class AuditLogsResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AuditLogsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/cloudflare/cloudflare-python#accessing-raw-response-data-eg-headers
+ """
+ return AuditLogsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AuditLogsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/cloudflare/cloudflare-python#with_streaming_response
+ """
+ return AuditLogsResourceWithStreamingResponse(self)
+
+ def list(
+ self,
+ *,
+ account_id: str | Omit = omit,
+ zone_id: str | Omit = omit,
+ before: Union[str, datetime] | Omit = omit,
+ changed_by: str | Omit = omit,
+ cursor: str | Omit = omit,
+ field_changed: Literal["status", "user_classification"] | Omit = omit,
+ order: Literal["asc", "desc"] | Omit = omit,
+ per_page: int | Omit = omit,
+ since: Union[str, datetime] | 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,
+ ) -> SyncCursorPagination[AuditLogListResponse]:
+ """
+ Lists audit log entries for all Security Center insights in the account or zone,
+ showing changes to insight status and classification.
+
+ Args:
+ account_id: The Account ID to use for this endpoint. Mutually exclusive with the Zone ID.
+
+ zone_id: The Zone ID to use for this endpoint. Mutually exclusive with the Account ID.
+
+ before: Filter entries changed before this timestamp (RFC 3339).
+
+ changed_by: Filter by the actor that made the change.
+
+ cursor: Opaque cursor for pagination. Use the cursor value from result_info of the
+ previous response.
+
+ field_changed: Filter by the field that was changed.
+
+ order: Sort order for results. Use 'asc' for oldest first or 'desc' for newest first.
+
+ per_page: Number of results per page.
+
+ since: Filter entries changed at or after this timestamp (RFC 3339).
+
+ 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 account_id and zone_id:
+ raise ValueError("You cannot provide both account_id and zone_id")
+
+ if account_id:
+ account_or_zone = "accounts"
+ account_or_zone_id = account_id
+ else:
+ if not zone_id:
+ raise ValueError("You must provide either account_id or zone_id")
+
+ account_or_zone = "zones"
+ account_or_zone_id = zone_id
+ return self._get_api_list(
+ path_template(
+ "/{account_or_zone}/{account_or_zone_id}/security-center/insights/audit-log",
+ account_or_zone=account_or_zone,
+ account_or_zone_id=account_or_zone_id,
+ ),
+ page=SyncCursorPagination[AuditLogListResponse],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "before": before,
+ "changed_by": changed_by,
+ "cursor": cursor,
+ "field_changed": field_changed,
+ "order": order,
+ "per_page": per_page,
+ "since": since,
+ },
+ audit_log_list_params.AuditLogListParams,
+ ),
+ ),
+ model=AuditLogListResponse,
+ )
+
+ def list_by_insight(
+ self,
+ issue_id: str,
+ *,
+ account_id: str | Omit = omit,
+ zone_id: str | Omit = omit,
+ before: Union[str, datetime] | Omit = omit,
+ changed_by: str | Omit = omit,
+ cursor: str | Omit = omit,
+ field_changed: Literal["status", "user_classification"] | Omit = omit,
+ order: Literal["asc", "desc"] | Omit = omit,
+ per_page: int | Omit = omit,
+ since: Union[str, datetime] | 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,
+ ) -> SyncCursorPagination[AuditLogListByInsightResponse]:
+ """
+ Lists audit log entries for a specific Security Center insight, showing changes
+ to its status and classification over time.
+
+ Args:
+ account_id: The Account ID to use for this endpoint. Mutually exclusive with the Zone ID.
+
+ zone_id: The Zone ID to use for this endpoint. Mutually exclusive with the Account ID.
+
+ before: Filter entries changed before this timestamp (RFC 3339).
+
+ changed_by: Filter by the actor that made the change.
+
+ cursor: Opaque cursor for pagination. Use the cursor value from result_info of the
+ previous response.
+
+ field_changed: Filter by the field that was changed.
+
+ order: Sort order for results. Use 'asc' for oldest first or 'desc' for newest first.
+
+ per_page: Number of results per page.
+
+ since: Filter entries changed at or after this timestamp (RFC 3339).
+
+ 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 issue_id:
+ raise ValueError(f"Expected a non-empty value for `issue_id` but received {issue_id!r}")
+ if account_id and zone_id:
+ raise ValueError("You cannot provide both account_id and zone_id")
+
+ if account_id:
+ account_or_zone = "accounts"
+ account_or_zone_id = account_id
+ else:
+ if not zone_id:
+ raise ValueError("You must provide either account_id or zone_id")
+
+ account_or_zone = "zones"
+ account_or_zone_id = zone_id
+ return self._get_api_list(
+ path_template(
+ "/{account_or_zone}/{account_or_zone_id}/security-center/insights/{issue_id}/audit-log",
+ issue_id=issue_id,
+ account_or_zone=account_or_zone,
+ account_or_zone_id=account_or_zone_id,
+ ),
+ page=SyncCursorPagination[AuditLogListByInsightResponse],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "before": before,
+ "changed_by": changed_by,
+ "cursor": cursor,
+ "field_changed": field_changed,
+ "order": order,
+ "per_page": per_page,
+ "since": since,
+ },
+ audit_log_list_by_insight_params.AuditLogListByInsightParams,
+ ),
+ ),
+ model=AuditLogListByInsightResponse,
+ )
+
+
+class AsyncAuditLogsResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncAuditLogsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/cloudflare/cloudflare-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncAuditLogsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncAuditLogsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/cloudflare/cloudflare-python#with_streaming_response
+ """
+ return AsyncAuditLogsResourceWithStreamingResponse(self)
+
+ def list(
+ self,
+ *,
+ account_id: str | Omit = omit,
+ zone_id: str | Omit = omit,
+ before: Union[str, datetime] | Omit = omit,
+ changed_by: str | Omit = omit,
+ cursor: str | Omit = omit,
+ field_changed: Literal["status", "user_classification"] | Omit = omit,
+ order: Literal["asc", "desc"] | Omit = omit,
+ per_page: int | Omit = omit,
+ since: Union[str, datetime] | 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,
+ ) -> AsyncPaginator[AuditLogListResponse, AsyncCursorPagination[AuditLogListResponse]]:
+ """
+ Lists audit log entries for all Security Center insights in the account or zone,
+ showing changes to insight status and classification.
+
+ Args:
+ account_id: The Account ID to use for this endpoint. Mutually exclusive with the Zone ID.
+
+ zone_id: The Zone ID to use for this endpoint. Mutually exclusive with the Account ID.
+
+ before: Filter entries changed before this timestamp (RFC 3339).
+
+ changed_by: Filter by the actor that made the change.
+
+ cursor: Opaque cursor for pagination. Use the cursor value from result_info of the
+ previous response.
+
+ field_changed: Filter by the field that was changed.
+
+ order: Sort order for results. Use 'asc' for oldest first or 'desc' for newest first.
+
+ per_page: Number of results per page.
+
+ since: Filter entries changed at or after this timestamp (RFC 3339).
+
+ 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 account_id and zone_id:
+ raise ValueError("You cannot provide both account_id and zone_id")
+
+ if account_id:
+ account_or_zone = "accounts"
+ account_or_zone_id = account_id
+ else:
+ if not zone_id:
+ raise ValueError("You must provide either account_id or zone_id")
+
+ account_or_zone = "zones"
+ account_or_zone_id = zone_id
+ return self._get_api_list(
+ path_template(
+ "/{account_or_zone}/{account_or_zone_id}/security-center/insights/audit-log",
+ account_or_zone=account_or_zone,
+ account_or_zone_id=account_or_zone_id,
+ ),
+ page=AsyncCursorPagination[AuditLogListResponse],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "before": before,
+ "changed_by": changed_by,
+ "cursor": cursor,
+ "field_changed": field_changed,
+ "order": order,
+ "per_page": per_page,
+ "since": since,
+ },
+ audit_log_list_params.AuditLogListParams,
+ ),
+ ),
+ model=AuditLogListResponse,
+ )
+
+ def list_by_insight(
+ self,
+ issue_id: str,
+ *,
+ account_id: str | Omit = omit,
+ zone_id: str | Omit = omit,
+ before: Union[str, datetime] | Omit = omit,
+ changed_by: str | Omit = omit,
+ cursor: str | Omit = omit,
+ field_changed: Literal["status", "user_classification"] | Omit = omit,
+ order: Literal["asc", "desc"] | Omit = omit,
+ per_page: int | Omit = omit,
+ since: Union[str, datetime] | 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,
+ ) -> AsyncPaginator[AuditLogListByInsightResponse, AsyncCursorPagination[AuditLogListByInsightResponse]]:
+ """
+ Lists audit log entries for a specific Security Center insight, showing changes
+ to its status and classification over time.
+
+ Args:
+ account_id: The Account ID to use for this endpoint. Mutually exclusive with the Zone ID.
+
+ zone_id: The Zone ID to use for this endpoint. Mutually exclusive with the Account ID.
+
+ before: Filter entries changed before this timestamp (RFC 3339).
+
+ changed_by: Filter by the actor that made the change.
+
+ cursor: Opaque cursor for pagination. Use the cursor value from result_info of the
+ previous response.
+
+ field_changed: Filter by the field that was changed.
+
+ order: Sort order for results. Use 'asc' for oldest first or 'desc' for newest first.
+
+ per_page: Number of results per page.
+
+ since: Filter entries changed at or after this timestamp (RFC 3339).
+
+ 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 issue_id:
+ raise ValueError(f"Expected a non-empty value for `issue_id` but received {issue_id!r}")
+ if account_id and zone_id:
+ raise ValueError("You cannot provide both account_id and zone_id")
+
+ if account_id:
+ account_or_zone = "accounts"
+ account_or_zone_id = account_id
+ else:
+ if not zone_id:
+ raise ValueError("You must provide either account_id or zone_id")
+
+ account_or_zone = "zones"
+ account_or_zone_id = zone_id
+ return self._get_api_list(
+ path_template(
+ "/{account_or_zone}/{account_or_zone_id}/security-center/insights/{issue_id}/audit-log",
+ issue_id=issue_id,
+ account_or_zone=account_or_zone,
+ account_or_zone_id=account_or_zone_id,
+ ),
+ page=AsyncCursorPagination[AuditLogListByInsightResponse],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "before": before,
+ "changed_by": changed_by,
+ "cursor": cursor,
+ "field_changed": field_changed,
+ "order": order,
+ "per_page": per_page,
+ "since": since,
+ },
+ audit_log_list_by_insight_params.AuditLogListByInsightParams,
+ ),
+ ),
+ model=AuditLogListByInsightResponse,
+ )
+
+
+class AuditLogsResourceWithRawResponse:
+ def __init__(self, audit_logs: AuditLogsResource) -> None:
+ self._audit_logs = audit_logs
+
+ self.list = to_raw_response_wrapper(
+ audit_logs.list,
+ )
+ self.list_by_insight = to_raw_response_wrapper(
+ audit_logs.list_by_insight,
+ )
+
+
+class AsyncAuditLogsResourceWithRawResponse:
+ def __init__(self, audit_logs: AsyncAuditLogsResource) -> None:
+ self._audit_logs = audit_logs
+
+ self.list = async_to_raw_response_wrapper(
+ audit_logs.list,
+ )
+ self.list_by_insight = async_to_raw_response_wrapper(
+ audit_logs.list_by_insight,
+ )
+
+
+class AuditLogsResourceWithStreamingResponse:
+ def __init__(self, audit_logs: AuditLogsResource) -> None:
+ self._audit_logs = audit_logs
+
+ self.list = to_streamed_response_wrapper(
+ audit_logs.list,
+ )
+ self.list_by_insight = to_streamed_response_wrapper(
+ audit_logs.list_by_insight,
+ )
+
+
+class AsyncAuditLogsResourceWithStreamingResponse:
+ def __init__(self, audit_logs: AsyncAuditLogsResource) -> None:
+ self._audit_logs = audit_logs
+
+ self.list = async_to_streamed_response_wrapper(
+ audit_logs.list,
+ )
+ self.list_by_insight = async_to_streamed_response_wrapper(
+ audit_logs.list_by_insight,
+ )
diff --git a/src/cloudflare/resources/security_center/insights/classification.py b/src/cloudflare/resources/security_center/insights/classification.py
new file mode 100644
index 00000000000..f866cdf9875
--- /dev/null
+++ b/src/cloudflare/resources/security_center/insights/classification.py
@@ -0,0 +1,250 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal
+
+import httpx
+
+from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ...._utils import path_template, maybe_transform, async_maybe_transform
+from ...._compat import cached_property
+from ...._resource import SyncAPIResource, AsyncAPIResource
+from ...._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ...._base_client import make_request_options
+from ....types.security_center.insights import classification_update_params
+from ....types.security_center.insights.classification_update_response import ClassificationUpdateResponse
+
+__all__ = ["ClassificationResource", "AsyncClassificationResource"]
+
+
+class ClassificationResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> ClassificationResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/cloudflare/cloudflare-python#accessing-raw-response-data-eg-headers
+ """
+ return ClassificationResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> ClassificationResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/cloudflare/cloudflare-python#with_streaming_response
+ """
+ return ClassificationResourceWithStreamingResponse(self)
+
+ def update(
+ self,
+ issue_id: str,
+ *,
+ account_id: str | Omit = omit,
+ zone_id: str | Omit = omit,
+ classification: Optional[Literal["false_positive", "accept_risk", "other"]] | Omit = omit,
+ rationale: 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,
+ ) -> ClassificationUpdateResponse:
+ """Updates the user classification for a Security Center insight.
+
+ Valid values are
+ 'false_positive' or 'accept_risk'. To reset, set classification to null. Cannot
+ change directly between classification values - must reset to null first.
+
+ Args:
+ account_id: The Account ID to use for this endpoint. Mutually exclusive with the Zone ID.
+
+ zone_id: The Zone ID to use for this endpoint. Mutually exclusive with the Account ID.
+
+ classification: User-defined classification for the insight. Can be 'false_positive',
+ 'accept_risk', 'other', or null.
+
+ rationale: Rationale for the classification change. Required when classification is
+ 'accept_risk' or 'other'.
+
+ 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 issue_id:
+ raise ValueError(f"Expected a non-empty value for `issue_id` but received {issue_id!r}")
+ if account_id and zone_id:
+ raise ValueError("You cannot provide both account_id and zone_id")
+
+ if account_id:
+ account_or_zone = "accounts"
+ account_or_zone_id = account_id
+ else:
+ if not zone_id:
+ raise ValueError("You must provide either account_id or zone_id")
+
+ account_or_zone = "zones"
+ account_or_zone_id = zone_id
+ return self._patch(
+ path_template(
+ "/{account_or_zone}/{account_or_zone_id}/security-center/insights/{issue_id}/classification",
+ issue_id=issue_id,
+ account_or_zone=account_or_zone,
+ account_or_zone_id=account_or_zone_id,
+ ),
+ body=maybe_transform(
+ {
+ "classification": classification,
+ "rationale": rationale,
+ },
+ classification_update_params.ClassificationUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ClassificationUpdateResponse,
+ )
+
+
+class AsyncClassificationResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncClassificationResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/cloudflare/cloudflare-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncClassificationResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncClassificationResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/cloudflare/cloudflare-python#with_streaming_response
+ """
+ return AsyncClassificationResourceWithStreamingResponse(self)
+
+ async def update(
+ self,
+ issue_id: str,
+ *,
+ account_id: str | Omit = omit,
+ zone_id: str | Omit = omit,
+ classification: Optional[Literal["false_positive", "accept_risk", "other"]] | Omit = omit,
+ rationale: 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,
+ ) -> ClassificationUpdateResponse:
+ """Updates the user classification for a Security Center insight.
+
+ Valid values are
+ 'false_positive' or 'accept_risk'. To reset, set classification to null. Cannot
+ change directly between classification values - must reset to null first.
+
+ Args:
+ account_id: The Account ID to use for this endpoint. Mutually exclusive with the Zone ID.
+
+ zone_id: The Zone ID to use for this endpoint. Mutually exclusive with the Account ID.
+
+ classification: User-defined classification for the insight. Can be 'false_positive',
+ 'accept_risk', 'other', or null.
+
+ rationale: Rationale for the classification change. Required when classification is
+ 'accept_risk' or 'other'.
+
+ 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 issue_id:
+ raise ValueError(f"Expected a non-empty value for `issue_id` but received {issue_id!r}")
+ if account_id and zone_id:
+ raise ValueError("You cannot provide both account_id and zone_id")
+
+ if account_id:
+ account_or_zone = "accounts"
+ account_or_zone_id = account_id
+ else:
+ if not zone_id:
+ raise ValueError("You must provide either account_id or zone_id")
+
+ account_or_zone = "zones"
+ account_or_zone_id = zone_id
+ return await self._patch(
+ path_template(
+ "/{account_or_zone}/{account_or_zone_id}/security-center/insights/{issue_id}/classification",
+ issue_id=issue_id,
+ account_or_zone=account_or_zone,
+ account_or_zone_id=account_or_zone_id,
+ ),
+ body=await async_maybe_transform(
+ {
+ "classification": classification,
+ "rationale": rationale,
+ },
+ classification_update_params.ClassificationUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ClassificationUpdateResponse,
+ )
+
+
+class ClassificationResourceWithRawResponse:
+ def __init__(self, classification: ClassificationResource) -> None:
+ self._classification = classification
+
+ self.update = to_raw_response_wrapper(
+ classification.update,
+ )
+
+
+class AsyncClassificationResourceWithRawResponse:
+ def __init__(self, classification: AsyncClassificationResource) -> None:
+ self._classification = classification
+
+ self.update = async_to_raw_response_wrapper(
+ classification.update,
+ )
+
+
+class ClassificationResourceWithStreamingResponse:
+ def __init__(self, classification: ClassificationResource) -> None:
+ self._classification = classification
+
+ self.update = to_streamed_response_wrapper(
+ classification.update,
+ )
+
+
+class AsyncClassificationResourceWithStreamingResponse:
+ def __init__(self, classification: AsyncClassificationResource) -> None:
+ self._classification = classification
+
+ self.update = async_to_streamed_response_wrapper(
+ classification.update,
+ )
diff --git a/src/cloudflare/resources/security_center/insights/context.py b/src/cloudflare/resources/security_center/insights/context.py
new file mode 100644
index 00000000000..cbf4382ffca
--- /dev/null
+++ b/src/cloudflare/resources/security_center/insights/context.py
@@ -0,0 +1,197 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Type, Optional, cast
+
+import httpx
+
+from ...._types import Body, Query, Headers, NotGiven, not_given
+from ...._utils import path_template
+from ...._compat import cached_property
+from ...._resource import SyncAPIResource, AsyncAPIResource
+from ...._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ...._wrappers import ResultWrapper
+from ...._base_client import make_request_options
+from ....types.security_center.insights.context_get_response import ContextGetResponse
+
+__all__ = ["ContextResource", "AsyncContextResource"]
+
+
+class ContextResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> ContextResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/cloudflare/cloudflare-python#accessing-raw-response-data-eg-headers
+ """
+ return ContextResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> ContextResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/cloudflare/cloudflare-python#with_streaming_response
+ """
+ return ContextResourceWithStreamingResponse(self)
+
+ def get(
+ self,
+ issue_id: str,
+ *,
+ account_id: str,
+ # 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,
+ ) -> Optional[ContextGetResponse]:
+ """Returns the full context payload for an insight.
+
+ This endpoint is used for
+ insights with large payloads that are not included inline in the list response.
+
+ Args:
+ account_id: Identifier.
+
+ 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 account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ if not issue_id:
+ raise ValueError(f"Expected a non-empty value for `issue_id` but received {issue_id!r}")
+ return self._get(
+ path_template(
+ "/accounts/{account_id}/security-center/insights/{issue_id}/context",
+ account_id=account_id,
+ issue_id=issue_id,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ post_parser=ResultWrapper[Optional[ContextGetResponse]]._unwrapper,
+ ),
+ cast_to=cast(Type[Optional[ContextGetResponse]], ResultWrapper[ContextGetResponse]),
+ )
+
+
+class AsyncContextResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncContextResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/cloudflare/cloudflare-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncContextResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncContextResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/cloudflare/cloudflare-python#with_streaming_response
+ """
+ return AsyncContextResourceWithStreamingResponse(self)
+
+ async def get(
+ self,
+ issue_id: str,
+ *,
+ account_id: str,
+ # 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,
+ ) -> Optional[ContextGetResponse]:
+ """Returns the full context payload for an insight.
+
+ This endpoint is used for
+ insights with large payloads that are not included inline in the list response.
+
+ Args:
+ account_id: Identifier.
+
+ 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 account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ if not issue_id:
+ raise ValueError(f"Expected a non-empty value for `issue_id` but received {issue_id!r}")
+ return await self._get(
+ path_template(
+ "/accounts/{account_id}/security-center/insights/{issue_id}/context",
+ account_id=account_id,
+ issue_id=issue_id,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ post_parser=ResultWrapper[Optional[ContextGetResponse]]._unwrapper,
+ ),
+ cast_to=cast(Type[Optional[ContextGetResponse]], ResultWrapper[ContextGetResponse]),
+ )
+
+
+class ContextResourceWithRawResponse:
+ def __init__(self, context: ContextResource) -> None:
+ self._context = context
+
+ self.get = to_raw_response_wrapper(
+ context.get,
+ )
+
+
+class AsyncContextResourceWithRawResponse:
+ def __init__(self, context: AsyncContextResource) -> None:
+ self._context = context
+
+ self.get = async_to_raw_response_wrapper(
+ context.get,
+ )
+
+
+class ContextResourceWithStreamingResponse:
+ def __init__(self, context: ContextResource) -> None:
+ self._context = context
+
+ self.get = to_streamed_response_wrapper(
+ context.get,
+ )
+
+
+class AsyncContextResourceWithStreamingResponse:
+ def __init__(self, context: AsyncContextResource) -> None:
+ self._context = context
+
+ self.get = async_to_streamed_response_wrapper(
+ context.get,
+ )
diff --git a/src/cloudflare/resources/security_center/insights/insights.py b/src/cloudflare/resources/security_center/insights/insights.py
index 8ccae5088ef..b797de82013 100644
--- a/src/cloudflare/resources/security_center/insights/insights.py
+++ b/src/cloudflare/resources/security_center/insights/insights.py
@@ -22,6 +22,14 @@
ClassResourceWithStreamingResponse,
AsyncClassResourceWithStreamingResponse,
)
+from .context import (
+ ContextResource,
+ AsyncContextResource,
+ ContextResourceWithRawResponse,
+ AsyncContextResourceWithRawResponse,
+ ContextResourceWithStreamingResponse,
+ AsyncContextResourceWithStreamingResponse,
+)
from .severity import (
SeverityResource,
AsyncSeverityResource,
@@ -33,6 +41,14 @@
from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given
from ...._utils import path_template, maybe_transform, async_maybe_transform
from ...._compat import cached_property
+from .audit_logs import (
+ AuditLogsResource,
+ AsyncAuditLogsResource,
+ AuditLogsResourceWithRawResponse,
+ AsyncAuditLogsResourceWithRawResponse,
+ AuditLogsResourceWithStreamingResponse,
+ AsyncAuditLogsResourceWithStreamingResponse,
+)
from ...._resource import SyncAPIResource, AsyncAPIResource
from ...._response import (
to_raw_response_wrapper,
@@ -41,6 +57,14 @@
async_to_streamed_response_wrapper,
)
from ....pagination import SyncV4PagePagination, AsyncV4PagePagination
+from .classification import (
+ ClassificationResource,
+ AsyncClassificationResource,
+ ClassificationResourceWithRawResponse,
+ AsyncClassificationResourceWithRawResponse,
+ ClassificationResourceWithStreamingResponse,
+ AsyncClassificationResourceWithStreamingResponse,
+)
from ...._base_client import AsyncPaginator, make_request_options
from ....types.security_center import insight_list_params, insight_dismiss_params
from ....types.security_center.insight_list_response import InsightListResponse
@@ -64,6 +88,18 @@ def severity(self) -> SeverityResource:
def type(self) -> TypeResource:
return TypeResource(self._client)
+ @cached_property
+ def audit_logs(self) -> AuditLogsResource:
+ return AuditLogsResource(self._client)
+
+ @cached_property
+ def classification(self) -> ClassificationResource:
+ return ClassificationResource(self._client)
+
+ @cached_property
+ def context(self) -> ContextResource:
+ return ContextResource(self._client)
+
@cached_property
def with_raw_response(self) -> InsightsResourceWithRawResponse:
"""
@@ -248,6 +284,18 @@ def severity(self) -> AsyncSeverityResource:
def type(self) -> AsyncTypeResource:
return AsyncTypeResource(self._client)
+ @cached_property
+ def audit_logs(self) -> AsyncAuditLogsResource:
+ return AsyncAuditLogsResource(self._client)
+
+ @cached_property
+ def classification(self) -> AsyncClassificationResource:
+ return AsyncClassificationResource(self._client)
+
+ @cached_property
+ def context(self) -> AsyncContextResource:
+ return AsyncContextResource(self._client)
+
@cached_property
def with_raw_response(self) -> AsyncInsightsResourceWithRawResponse:
"""
@@ -442,6 +490,18 @@ def severity(self) -> SeverityResourceWithRawResponse:
def type(self) -> TypeResourceWithRawResponse:
return TypeResourceWithRawResponse(self._insights.type)
+ @cached_property
+ def audit_logs(self) -> AuditLogsResourceWithRawResponse:
+ return AuditLogsResourceWithRawResponse(self._insights.audit_logs)
+
+ @cached_property
+ def classification(self) -> ClassificationResourceWithRawResponse:
+ return ClassificationResourceWithRawResponse(self._insights.classification)
+
+ @cached_property
+ def context(self) -> ContextResourceWithRawResponse:
+ return ContextResourceWithRawResponse(self._insights.context)
+
class AsyncInsightsResourceWithRawResponse:
def __init__(self, insights: AsyncInsightsResource) -> None:
@@ -466,6 +526,18 @@ def severity(self) -> AsyncSeverityResourceWithRawResponse:
def type(self) -> AsyncTypeResourceWithRawResponse:
return AsyncTypeResourceWithRawResponse(self._insights.type)
+ @cached_property
+ def audit_logs(self) -> AsyncAuditLogsResourceWithRawResponse:
+ return AsyncAuditLogsResourceWithRawResponse(self._insights.audit_logs)
+
+ @cached_property
+ def classification(self) -> AsyncClassificationResourceWithRawResponse:
+ return AsyncClassificationResourceWithRawResponse(self._insights.classification)
+
+ @cached_property
+ def context(self) -> AsyncContextResourceWithRawResponse:
+ return AsyncContextResourceWithRawResponse(self._insights.context)
+
class InsightsResourceWithStreamingResponse:
def __init__(self, insights: InsightsResource) -> None:
@@ -490,6 +562,18 @@ def severity(self) -> SeverityResourceWithStreamingResponse:
def type(self) -> TypeResourceWithStreamingResponse:
return TypeResourceWithStreamingResponse(self._insights.type)
+ @cached_property
+ def audit_logs(self) -> AuditLogsResourceWithStreamingResponse:
+ return AuditLogsResourceWithStreamingResponse(self._insights.audit_logs)
+
+ @cached_property
+ def classification(self) -> ClassificationResourceWithStreamingResponse:
+ return ClassificationResourceWithStreamingResponse(self._insights.classification)
+
+ @cached_property
+ def context(self) -> ContextResourceWithStreamingResponse:
+ return ContextResourceWithStreamingResponse(self._insights.context)
+
class AsyncInsightsResourceWithStreamingResponse:
def __init__(self, insights: AsyncInsightsResource) -> None:
@@ -513,3 +597,15 @@ def severity(self) -> AsyncSeverityResourceWithStreamingResponse:
@cached_property
def type(self) -> AsyncTypeResourceWithStreamingResponse:
return AsyncTypeResourceWithStreamingResponse(self._insights.type)
+
+ @cached_property
+ def audit_logs(self) -> AsyncAuditLogsResourceWithStreamingResponse:
+ return AsyncAuditLogsResourceWithStreamingResponse(self._insights.audit_logs)
+
+ @cached_property
+ def classification(self) -> AsyncClassificationResourceWithStreamingResponse:
+ return AsyncClassificationResourceWithStreamingResponse(self._insights.classification)
+
+ @cached_property
+ def context(self) -> AsyncContextResourceWithStreamingResponse:
+ return AsyncContextResourceWithStreamingResponse(self._insights.context)
diff --git a/src/cloudflare/resources/zero_trust/access/logs/access_requests.py b/src/cloudflare/resources/zero_trust/access/logs/access_requests.py
index 06b0f797e51..cd871420c10 100644
--- a/src/cloudflare/resources/zero_trust/access/logs/access_requests.py
+++ b/src/cloudflare/resources/zero_trust/access/logs/access_requests.py
@@ -57,7 +57,7 @@ def list(
direction: Literal["desc", "asc"] | Omit = omit,
email: str | Omit = omit,
email_exact: bool | Omit = omit,
- email_op: Literal["eq", "neq"] | Omit = omit,
+ email_op: Literal["eq", "neq", "contains"] | Omit = omit,
fields: str | Omit = omit,
idp_op: Literal["eq", "neq"] | Omit = omit,
limit: int | Omit = omit,
@@ -92,14 +92,23 @@ def list(
direction: The chronological sorting order for the logs.
- email: Filter by user email. Defaults to substring matching. To force exact matching,
- set `email_exact=true`. Example (default): `email=@example.com` returns all
- events with that domain. Example (exact):
- `email=user@example.com&email_exact=true` returns only that user.
+ email: Filter by user email. Match mode is controlled by `emailOp` (preferred) or the
+ legacy `email_exact` flag.
+
+ - Default (no `emailOp`, `email_exact=false` or unset): substring match —
+ `email=@example.com` returns all events with that domain.
+ - Exact match: set `emailOp=eq` (preferred) or `email_exact=true` — e.g.
+ `email=user@example.com&email_exact=true` returns only that user.
+ - Explicit substring match: set `emailOp=contains` (without `email_exact=true`).
+ When both are set, `email_exact=true` takes precedence and the match is exact.
+ - Exclusion: set `emailOp=neq`. With `email_exact=true` this is an exact-value
+ exclusion; without it, a fuzzy substring exclusion.
email_exact: When true, `email` is matched exactly instead of substring matching.
- email_op: Operator for the `email` filter.
+ email_op: Operator for the `email` filter. `contains` performs a substring
+ (case-sensitive) match. When `email_exact=true` is also set, `email_exact` takes
+ precedence and `contains` is ignored.
fields: Comma-separated list of fields to include in the response. When omitted, all
fields are returned.
@@ -120,9 +129,11 @@ def list(
until: The latest event timestamp to query.
- user_id: Filter by user UUID.
+ user_id: Deprecated. Accepted for backward compatibility but no longer applied as a
+ filter. Use `email` instead.
- user_id_op: Operator for the `user_id` filter.
+ user_id_op: Deprecated. Accepted for backward compatibility but no longer applied as a
+ filter (the `user_id` parameter is itself deprecated).
extra_headers: Send extra headers
@@ -202,7 +213,7 @@ async def list(
direction: Literal["desc", "asc"] | Omit = omit,
email: str | Omit = omit,
email_exact: bool | Omit = omit,
- email_op: Literal["eq", "neq"] | Omit = omit,
+ email_op: Literal["eq", "neq", "contains"] | Omit = omit,
fields: str | Omit = omit,
idp_op: Literal["eq", "neq"] | Omit = omit,
limit: int | Omit = omit,
@@ -237,14 +248,23 @@ async def list(
direction: The chronological sorting order for the logs.
- email: Filter by user email. Defaults to substring matching. To force exact matching,
- set `email_exact=true`. Example (default): `email=@example.com` returns all
- events with that domain. Example (exact):
- `email=user@example.com&email_exact=true` returns only that user.
+ email: Filter by user email. Match mode is controlled by `emailOp` (preferred) or the
+ legacy `email_exact` flag.
+
+ - Default (no `emailOp`, `email_exact=false` or unset): substring match —
+ `email=@example.com` returns all events with that domain.
+ - Exact match: set `emailOp=eq` (preferred) or `email_exact=true` — e.g.
+ `email=user@example.com&email_exact=true` returns only that user.
+ - Explicit substring match: set `emailOp=contains` (without `email_exact=true`).
+ When both are set, `email_exact=true` takes precedence and the match is exact.
+ - Exclusion: set `emailOp=neq`. With `email_exact=true` this is an exact-value
+ exclusion; without it, a fuzzy substring exclusion.
email_exact: When true, `email` is matched exactly instead of substring matching.
- email_op: Operator for the `email` filter.
+ email_op: Operator for the `email` filter. `contains` performs a substring
+ (case-sensitive) match. When `email_exact=true` is also set, `email_exact` takes
+ precedence and `contains` is ignored.
fields: Comma-separated list of fields to include in the response. When omitted, all
fields are returned.
@@ -265,9 +285,11 @@ async def list(
until: The latest event timestamp to query.
- user_id: Filter by user UUID.
+ user_id: Deprecated. Accepted for backward compatibility but no longer applied as a
+ filter. Use `email` instead.
- user_id_op: Operator for the `user_id` filter.
+ user_id_op: Deprecated. Accepted for backward compatibility but no longer applied as a
+ filter (the `user_id` parameter is itself deprecated).
extra_headers: Send extra headers
diff --git a/src/cloudflare/resources/zero_trust/access/logs/scim/updates.py b/src/cloudflare/resources/zero_trust/access/logs/scim/updates.py
index ce98c696427..df4556789ed 100644
--- a/src/cloudflare/resources/zero_trust/access/logs/scim/updates.py
+++ b/src/cloudflare/resources/zero_trust/access/logs/scim/updates.py
@@ -51,16 +51,16 @@ def list(
*,
account_id: str,
idp_id: SequenceNotStr[str],
- cf_resource_id: str | Omit = omit,
+ cf_resource_id: SequenceNotStr[str] | Omit = omit,
direction: Literal["desc", "asc"] | Omit = omit,
- idp_resource_id: str | Omit = omit,
+ idp_resource_id: SequenceNotStr[str] | Omit = omit,
limit: int | Omit = omit,
page: int | Omit = omit,
per_page: int | Omit = omit,
request_method: List[Literal["DELETE", "PATCH", "POST", "PUT"]] | Omit = omit,
- resource_group_name: str | Omit = omit,
+ resource_group_name: SequenceNotStr[str] | Omit = omit,
resource_type: List[Literal["USER", "GROUP"]] | Omit = omit,
- resource_user_email: str | Omit = omit,
+ resource_user_email: SequenceNotStr[str] | Omit = omit,
since: Union[str, datetime] | Omit = omit,
status: List[Literal["FAILURE", "SUCCESS"]] | Omit = omit,
until: Union[str, datetime] | Omit = omit,
@@ -81,11 +81,17 @@ def list(
idp_id: The unique Id of the IdP that has SCIM enabled.
- cf_resource_id: The unique Cloudflare-generated Id of the SCIM resource.
+ cf_resource_id: The unique Cloudflare-generated Id of the SCIM resource. Pass once for a single
+ lookup (`?cf_resource_id=A`) or repeat the parameter
+ (`?cf_resource_id=A&cf_resource_id=B`) to filter by multiple resources in one
+ request.
direction: The chronological order used to sort the logs.
- idp_resource_id: The IdP-generated Id of the SCIM resource.
+ idp_resource_id: The IdP-generated Id of the SCIM resource. Pass once for a single lookup
+ (`?idp_resource_id=A`) or repeat the parameter
+ (`?idp_resource_id=A&idp_resource_id=B`) to filter by multiple resources in one
+ request.
limit: The maximum number of update logs to retrieve.
@@ -95,11 +101,17 @@ def list(
request_method: The request method of the SCIM request.
- resource_group_name: The display name of the SCIM Group resource.
+ resource_group_name: The display name of the SCIM Group resource. Pass once for a single lookup
+ (`?resource_group_name=A`) or repeat the parameter
+ (`?resource_group_name=A&resource_group_name=B`) to filter by multiple group
+ names in one request.
resource_type: The resource type of the SCIM request.
- resource_user_email: The email address of the SCIM User resource.
+ resource_user_email: The email address of the SCIM User resource. Pass once for a single lookup
+ (`?resource_user_email=A`) or repeat the parameter
+ (`?resource_user_email=A&resource_user_email=B`) to filter by multiple emails in
+ one request.
since: the timestamp of the earliest update log.
@@ -174,16 +186,16 @@ def list(
*,
account_id: str,
idp_id: SequenceNotStr[str],
- cf_resource_id: str | Omit = omit,
+ cf_resource_id: SequenceNotStr[str] | Omit = omit,
direction: Literal["desc", "asc"] | Omit = omit,
- idp_resource_id: str | Omit = omit,
+ idp_resource_id: SequenceNotStr[str] | Omit = omit,
limit: int | Omit = omit,
page: int | Omit = omit,
per_page: int | Omit = omit,
request_method: List[Literal["DELETE", "PATCH", "POST", "PUT"]] | Omit = omit,
- resource_group_name: str | Omit = omit,
+ resource_group_name: SequenceNotStr[str] | Omit = omit,
resource_type: List[Literal["USER", "GROUP"]] | Omit = omit,
- resource_user_email: str | Omit = omit,
+ resource_user_email: SequenceNotStr[str] | Omit = omit,
since: Union[str, datetime] | Omit = omit,
status: List[Literal["FAILURE", "SUCCESS"]] | Omit = omit,
until: Union[str, datetime] | Omit = omit,
@@ -204,11 +216,17 @@ def list(
idp_id: The unique Id of the IdP that has SCIM enabled.
- cf_resource_id: The unique Cloudflare-generated Id of the SCIM resource.
+ cf_resource_id: The unique Cloudflare-generated Id of the SCIM resource. Pass once for a single
+ lookup (`?cf_resource_id=A`) or repeat the parameter
+ (`?cf_resource_id=A&cf_resource_id=B`) to filter by multiple resources in one
+ request.
direction: The chronological order used to sort the logs.
- idp_resource_id: The IdP-generated Id of the SCIM resource.
+ idp_resource_id: The IdP-generated Id of the SCIM resource. Pass once for a single lookup
+ (`?idp_resource_id=A`) or repeat the parameter
+ (`?idp_resource_id=A&idp_resource_id=B`) to filter by multiple resources in one
+ request.
limit: The maximum number of update logs to retrieve.
@@ -218,11 +236,17 @@ def list(
request_method: The request method of the SCIM request.
- resource_group_name: The display name of the SCIM Group resource.
+ resource_group_name: The display name of the SCIM Group resource. Pass once for a single lookup
+ (`?resource_group_name=A`) or repeat the parameter
+ (`?resource_group_name=A&resource_group_name=B`) to filter by multiple group
+ names in one request.
resource_type: The resource type of the SCIM request.
- resource_user_email: The email address of the SCIM User resource.
+ resource_user_email: The email address of the SCIM User resource. Pass once for a single lookup
+ (`?resource_user_email=A`) or repeat the parameter
+ (`?resource_user_email=A&resource_user_email=B`) to filter by multiple emails in
+ one request.
since: the timestamp of the earliest update log.
diff --git a/src/cloudflare/resources/zero_trust/api.md b/src/cloudflare/resources/zero_trust/api.md
index cedf199e090..35c0c1fd0d3 100644
--- a/src/cloudflare/resources/zero_trust/api.md
+++ b/src/cloudflare/resources/zero_trust/api.md
@@ -103,6 +103,22 @@ Methods:
- client.zero_trust.devices.ip_profiles.delete(profile_id, \*, account_id) -> IPProfileDeleteResponse
- client.zero_trust.devices.ip_profiles.get(profile_id, \*, account_id) -> IPProfile
+### DeploymentGroups
+
+Types:
+
+```python
+from cloudflare.types.zero_trust.devices import DeploymentGroup, DeploymentGroupDeleteResponse
+```
+
+Methods:
+
+- client.zero_trust.devices.deployment_groups.create(\*, account_id, \*\*params) -> DeploymentGroup
+- client.zero_trust.devices.deployment_groups.list(\*, account_id, \*\*params) -> SyncV4PagePaginationArray[DeploymentGroup]
+- client.zero_trust.devices.deployment_groups.delete(group_id, \*, account_id) -> DeploymentGroupDeleteResponse
+- client.zero_trust.devices.deployment_groups.edit(group_id, \*, account_id, \*\*params) -> DeploymentGroup
+- client.zero_trust.devices.deployment_groups.get(group_id, \*, account_id) -> DeploymentGroup
+
### Networks
Types:
diff --git a/src/cloudflare/resources/zero_trust/devices/__init__.py b/src/cloudflare/resources/zero_trust/devices/__init__.py
index a3579f08c06..45351e0fe62 100644
--- a/src/cloudflare/resources/zero_trust/devices/__init__.py
+++ b/src/cloudflare/resources/zero_trust/devices/__init__.py
@@ -104,6 +104,14 @@
OverrideCodesResourceWithStreamingResponse,
AsyncOverrideCodesResourceWithStreamingResponse,
)
+from .deployment_groups import (
+ DeploymentGroupsResource,
+ AsyncDeploymentGroupsResource,
+ DeploymentGroupsResourceWithRawResponse,
+ AsyncDeploymentGroupsResourceWithRawResponse,
+ DeploymentGroupsResourceWithStreamingResponse,
+ AsyncDeploymentGroupsResourceWithStreamingResponse,
+)
__all__ = [
"ResilienceResource",
@@ -130,6 +138,12 @@
"AsyncIPProfilesResourceWithRawResponse",
"IPProfilesResourceWithStreamingResponse",
"AsyncIPProfilesResourceWithStreamingResponse",
+ "DeploymentGroupsResource",
+ "AsyncDeploymentGroupsResource",
+ "DeploymentGroupsResourceWithRawResponse",
+ "AsyncDeploymentGroupsResourceWithRawResponse",
+ "DeploymentGroupsResourceWithStreamingResponse",
+ "AsyncDeploymentGroupsResourceWithStreamingResponse",
"NetworksResource",
"AsyncNetworksResource",
"NetworksResourceWithRawResponse",
diff --git a/src/cloudflare/resources/zero_trust/devices/deployment_groups.py b/src/cloudflare/resources/zero_trust/devices/deployment_groups.py
new file mode 100644
index 00000000000..12df51fa905
--- /dev/null
+++ b/src/cloudflare/resources/zero_trust/devices/deployment_groups.py
@@ -0,0 +1,668 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Type, Iterable, cast
+
+import httpx
+
+from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given
+from ...._utils import path_template, maybe_transform, async_maybe_transform
+from ...._compat import cached_property
+from ...._resource import SyncAPIResource, AsyncAPIResource
+from ...._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ...._wrappers import ResultWrapper
+from ....pagination import SyncV4PagePaginationArray, AsyncV4PagePaginationArray
+from ...._base_client import AsyncPaginator, make_request_options
+from ....types.zero_trust.devices import (
+ deployment_group_edit_params,
+ deployment_group_list_params,
+ deployment_group_create_params,
+)
+from ....types.zero_trust.devices.deployment_group import DeploymentGroup
+from ....types.zero_trust.devices.deployment_group_delete_response import DeploymentGroupDeleteResponse
+
+__all__ = ["DeploymentGroupsResource", "AsyncDeploymentGroupsResource"]
+
+
+class DeploymentGroupsResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> DeploymentGroupsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/cloudflare/cloudflare-python#accessing-raw-response-data-eg-headers
+ """
+ return DeploymentGroupsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> DeploymentGroupsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/cloudflare/cloudflare-python#with_streaming_response
+ """
+ return DeploymentGroupsResourceWithStreamingResponse(self)
+
+ def create(
+ self,
+ *,
+ account_id: str,
+ name: str,
+ version_config: Iterable[deployment_group_create_params.VersionConfig],
+ policy_ids: SequenceNotStr[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,
+ ) -> DeploymentGroup:
+ """Creates a new deployment group.
+
+ Policy IDs must be unique across all deployment
+ groups. This endpoint is in Beta.
+
+ Args:
+ name: A user-friendly name for the deployment group.
+
+ version_config: Contains at least one version configuration.
+
+ policy_ids: Contains an optional list of policy IDs assigned to a group.
+
+ 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 account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ return self._post(
+ path_template("/accounts/{account_id}/devices/deployment-groups", account_id=account_id),
+ body=maybe_transform(
+ {
+ "name": name,
+ "version_config": version_config,
+ "policy_ids": policy_ids,
+ },
+ deployment_group_create_params.DeploymentGroupCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ post_parser=ResultWrapper[DeploymentGroup]._unwrapper,
+ ),
+ cast_to=cast(Type[DeploymentGroup], ResultWrapper[DeploymentGroup]),
+ )
+
+ def list(
+ self,
+ *,
+ account_id: str,
+ page: int | Omit = omit,
+ per_page: int | 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,
+ ) -> SyncV4PagePaginationArray[DeploymentGroup]:
+ """Lists all deployment groups for an account.
+
+ Use deployment groups to assign
+ target WARP client versions to specific devices. This endpoint is in Beta.
+
+ Args:
+ page: The page number to return.
+
+ per_page: The maximum number of deployment groups to return per page.
+
+ 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 account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ return self._get_api_list(
+ path_template("/accounts/{account_id}/devices/deployment-groups", account_id=account_id),
+ page=SyncV4PagePaginationArray[DeploymentGroup],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "page": page,
+ "per_page": per_page,
+ },
+ deployment_group_list_params.DeploymentGroupListParams,
+ ),
+ ),
+ model=DeploymentGroup,
+ )
+
+ def delete(
+ self,
+ group_id: str,
+ *,
+ account_id: str,
+ # 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,
+ ) -> DeploymentGroupDeleteResponse:
+ """Deletes a deployment group.
+
+ Associated policies no longer apply and devices stop
+ receiving version targets. This endpoint is in Beta.
+
+ Args:
+ 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 account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ if not group_id:
+ raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}")
+ return self._delete(
+ path_template(
+ "/accounts/{account_id}/devices/deployment-groups/{group_id}", account_id=account_id, group_id=group_id
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ post_parser=ResultWrapper[DeploymentGroupDeleteResponse]._unwrapper,
+ ),
+ cast_to=cast(Type[DeploymentGroupDeleteResponse], ResultWrapper[DeploymentGroupDeleteResponse]),
+ )
+
+ def edit(
+ self,
+ group_id: str,
+ *,
+ account_id: str,
+ name: str | Omit = omit,
+ policy_ids: SequenceNotStr[str] | Omit = omit,
+ version_config: Iterable[deployment_group_edit_params.VersionConfig] | 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,
+ ) -> DeploymentGroup:
+ """Updates a deployment group.
+
+ Returns 409 if any newly added policy IDs already
+ belong to another deployment group. This endpoint is in Beta.
+
+ Args:
+ name: A user-friendly name for the deployment group.
+
+ policy_ids: Replaces the entire list of policy IDs.
+
+ version_config: Replaces the entire version_config array.
+
+ 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 account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ if not group_id:
+ raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}")
+ return self._patch(
+ path_template(
+ "/accounts/{account_id}/devices/deployment-groups/{group_id}", account_id=account_id, group_id=group_id
+ ),
+ body=maybe_transform(
+ {
+ "name": name,
+ "policy_ids": policy_ids,
+ "version_config": version_config,
+ },
+ deployment_group_edit_params.DeploymentGroupEditParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ post_parser=ResultWrapper[DeploymentGroup]._unwrapper,
+ ),
+ cast_to=cast(Type[DeploymentGroup], ResultWrapper[DeploymentGroup]),
+ )
+
+ def get(
+ self,
+ group_id: str,
+ *,
+ account_id: str,
+ # 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,
+ ) -> DeploymentGroup:
+ """Fetches a single deployment group by its ID.
+
+ This endpoint is in Beta.
+
+ Args:
+ 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 account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ if not group_id:
+ raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}")
+ return self._get(
+ path_template(
+ "/accounts/{account_id}/devices/deployment-groups/{group_id}", account_id=account_id, group_id=group_id
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ post_parser=ResultWrapper[DeploymentGroup]._unwrapper,
+ ),
+ cast_to=cast(Type[DeploymentGroup], ResultWrapper[DeploymentGroup]),
+ )
+
+
+class AsyncDeploymentGroupsResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncDeploymentGroupsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/cloudflare/cloudflare-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncDeploymentGroupsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncDeploymentGroupsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/cloudflare/cloudflare-python#with_streaming_response
+ """
+ return AsyncDeploymentGroupsResourceWithStreamingResponse(self)
+
+ async def create(
+ self,
+ *,
+ account_id: str,
+ name: str,
+ version_config: Iterable[deployment_group_create_params.VersionConfig],
+ policy_ids: SequenceNotStr[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,
+ ) -> DeploymentGroup:
+ """Creates a new deployment group.
+
+ Policy IDs must be unique across all deployment
+ groups. This endpoint is in Beta.
+
+ Args:
+ name: A user-friendly name for the deployment group.
+
+ version_config: Contains at least one version configuration.
+
+ policy_ids: Contains an optional list of policy IDs assigned to a group.
+
+ 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 account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ return await self._post(
+ path_template("/accounts/{account_id}/devices/deployment-groups", account_id=account_id),
+ body=await async_maybe_transform(
+ {
+ "name": name,
+ "version_config": version_config,
+ "policy_ids": policy_ids,
+ },
+ deployment_group_create_params.DeploymentGroupCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ post_parser=ResultWrapper[DeploymentGroup]._unwrapper,
+ ),
+ cast_to=cast(Type[DeploymentGroup], ResultWrapper[DeploymentGroup]),
+ )
+
+ def list(
+ self,
+ *,
+ account_id: str,
+ page: int | Omit = omit,
+ per_page: int | 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,
+ ) -> AsyncPaginator[DeploymentGroup, AsyncV4PagePaginationArray[DeploymentGroup]]:
+ """Lists all deployment groups for an account.
+
+ Use deployment groups to assign
+ target WARP client versions to specific devices. This endpoint is in Beta.
+
+ Args:
+ page: The page number to return.
+
+ per_page: The maximum number of deployment groups to return per page.
+
+ 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 account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ return self._get_api_list(
+ path_template("/accounts/{account_id}/devices/deployment-groups", account_id=account_id),
+ page=AsyncV4PagePaginationArray[DeploymentGroup],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "page": page,
+ "per_page": per_page,
+ },
+ deployment_group_list_params.DeploymentGroupListParams,
+ ),
+ ),
+ model=DeploymentGroup,
+ )
+
+ async def delete(
+ self,
+ group_id: str,
+ *,
+ account_id: str,
+ # 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,
+ ) -> DeploymentGroupDeleteResponse:
+ """Deletes a deployment group.
+
+ Associated policies no longer apply and devices stop
+ receiving version targets. This endpoint is in Beta.
+
+ Args:
+ 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 account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ if not group_id:
+ raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}")
+ return await self._delete(
+ path_template(
+ "/accounts/{account_id}/devices/deployment-groups/{group_id}", account_id=account_id, group_id=group_id
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ post_parser=ResultWrapper[DeploymentGroupDeleteResponse]._unwrapper,
+ ),
+ cast_to=cast(Type[DeploymentGroupDeleteResponse], ResultWrapper[DeploymentGroupDeleteResponse]),
+ )
+
+ async def edit(
+ self,
+ group_id: str,
+ *,
+ account_id: str,
+ name: str | Omit = omit,
+ policy_ids: SequenceNotStr[str] | Omit = omit,
+ version_config: Iterable[deployment_group_edit_params.VersionConfig] | 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,
+ ) -> DeploymentGroup:
+ """Updates a deployment group.
+
+ Returns 409 if any newly added policy IDs already
+ belong to another deployment group. This endpoint is in Beta.
+
+ Args:
+ name: A user-friendly name for the deployment group.
+
+ policy_ids: Replaces the entire list of policy IDs.
+
+ version_config: Replaces the entire version_config array.
+
+ 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 account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ if not group_id:
+ raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}")
+ return await self._patch(
+ path_template(
+ "/accounts/{account_id}/devices/deployment-groups/{group_id}", account_id=account_id, group_id=group_id
+ ),
+ body=await async_maybe_transform(
+ {
+ "name": name,
+ "policy_ids": policy_ids,
+ "version_config": version_config,
+ },
+ deployment_group_edit_params.DeploymentGroupEditParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ post_parser=ResultWrapper[DeploymentGroup]._unwrapper,
+ ),
+ cast_to=cast(Type[DeploymentGroup], ResultWrapper[DeploymentGroup]),
+ )
+
+ async def get(
+ self,
+ group_id: str,
+ *,
+ account_id: str,
+ # 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,
+ ) -> DeploymentGroup:
+ """Fetches a single deployment group by its ID.
+
+ This endpoint is in Beta.
+
+ Args:
+ 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 account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ if not group_id:
+ raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}")
+ return await self._get(
+ path_template(
+ "/accounts/{account_id}/devices/deployment-groups/{group_id}", account_id=account_id, group_id=group_id
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ post_parser=ResultWrapper[DeploymentGroup]._unwrapper,
+ ),
+ cast_to=cast(Type[DeploymentGroup], ResultWrapper[DeploymentGroup]),
+ )
+
+
+class DeploymentGroupsResourceWithRawResponse:
+ def __init__(self, deployment_groups: DeploymentGroupsResource) -> None:
+ self._deployment_groups = deployment_groups
+
+ self.create = to_raw_response_wrapper(
+ deployment_groups.create,
+ )
+ self.list = to_raw_response_wrapper(
+ deployment_groups.list,
+ )
+ self.delete = to_raw_response_wrapper(
+ deployment_groups.delete,
+ )
+ self.edit = to_raw_response_wrapper(
+ deployment_groups.edit,
+ )
+ self.get = to_raw_response_wrapper(
+ deployment_groups.get,
+ )
+
+
+class AsyncDeploymentGroupsResourceWithRawResponse:
+ def __init__(self, deployment_groups: AsyncDeploymentGroupsResource) -> None:
+ self._deployment_groups = deployment_groups
+
+ self.create = async_to_raw_response_wrapper(
+ deployment_groups.create,
+ )
+ self.list = async_to_raw_response_wrapper(
+ deployment_groups.list,
+ )
+ self.delete = async_to_raw_response_wrapper(
+ deployment_groups.delete,
+ )
+ self.edit = async_to_raw_response_wrapper(
+ deployment_groups.edit,
+ )
+ self.get = async_to_raw_response_wrapper(
+ deployment_groups.get,
+ )
+
+
+class DeploymentGroupsResourceWithStreamingResponse:
+ def __init__(self, deployment_groups: DeploymentGroupsResource) -> None:
+ self._deployment_groups = deployment_groups
+
+ self.create = to_streamed_response_wrapper(
+ deployment_groups.create,
+ )
+ self.list = to_streamed_response_wrapper(
+ deployment_groups.list,
+ )
+ self.delete = to_streamed_response_wrapper(
+ deployment_groups.delete,
+ )
+ self.edit = to_streamed_response_wrapper(
+ deployment_groups.edit,
+ )
+ self.get = to_streamed_response_wrapper(
+ deployment_groups.get,
+ )
+
+
+class AsyncDeploymentGroupsResourceWithStreamingResponse:
+ def __init__(self, deployment_groups: AsyncDeploymentGroupsResource) -> None:
+ self._deployment_groups = deployment_groups
+
+ self.create = async_to_streamed_response_wrapper(
+ deployment_groups.create,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ deployment_groups.list,
+ )
+ self.delete = async_to_streamed_response_wrapper(
+ deployment_groups.delete,
+ )
+ self.edit = async_to_streamed_response_wrapper(
+ deployment_groups.edit,
+ )
+ self.get = async_to_streamed_response_wrapper(
+ deployment_groups.get,
+ )
diff --git a/src/cloudflare/resources/zero_trust/devices/devices.py b/src/cloudflare/resources/zero_trust/devices/devices.py
index d94495d96a1..efd14729f02 100644
--- a/src/cloudflare/resources/zero_trust/devices/devices.py
+++ b/src/cloudflare/resources/zero_trust/devices/devices.py
@@ -101,6 +101,14 @@
PostureResourceWithStreamingResponse,
AsyncPostureResourceWithStreamingResponse,
)
+from .deployment_groups import (
+ DeploymentGroupsResource,
+ AsyncDeploymentGroupsResource,
+ DeploymentGroupsResourceWithRawResponse,
+ AsyncDeploymentGroupsResourceWithRawResponse,
+ DeploymentGroupsResourceWithStreamingResponse,
+ AsyncDeploymentGroupsResourceWithStreamingResponse,
+)
from .policies.policies import (
PoliciesResource,
AsyncPoliciesResource,
@@ -144,6 +152,10 @@ def dex_tests(self) -> DEXTestsResource:
def ip_profiles(self) -> IPProfilesResource:
return IPProfilesResource(self._client)
+ @cached_property
+ def deployment_groups(self) -> DeploymentGroupsResource:
+ return DeploymentGroupsResource(self._client)
+
@cached_property
def networks(self) -> NetworksResource:
return NetworksResource(self._client)
@@ -312,6 +324,10 @@ def dex_tests(self) -> AsyncDEXTestsResource:
def ip_profiles(self) -> AsyncIPProfilesResource:
return AsyncIPProfilesResource(self._client)
+ @cached_property
+ def deployment_groups(self) -> AsyncDeploymentGroupsResource:
+ return AsyncDeploymentGroupsResource(self._client)
+
@cached_property
def networks(self) -> AsyncNetworksResource:
return AsyncNetworksResource(self._client)
@@ -494,6 +510,10 @@ def dex_tests(self) -> DEXTestsResourceWithRawResponse:
def ip_profiles(self) -> IPProfilesResourceWithRawResponse:
return IPProfilesResourceWithRawResponse(self._devices.ip_profiles)
+ @cached_property
+ def deployment_groups(self) -> DeploymentGroupsResourceWithRawResponse:
+ return DeploymentGroupsResourceWithRawResponse(self._devices.deployment_groups)
+
@cached_property
def networks(self) -> NetworksResourceWithRawResponse:
return NetworksResourceWithRawResponse(self._devices.networks)
@@ -562,6 +582,10 @@ def dex_tests(self) -> AsyncDEXTestsResourceWithRawResponse:
def ip_profiles(self) -> AsyncIPProfilesResourceWithRawResponse:
return AsyncIPProfilesResourceWithRawResponse(self._devices.ip_profiles)
+ @cached_property
+ def deployment_groups(self) -> AsyncDeploymentGroupsResourceWithRawResponse:
+ return AsyncDeploymentGroupsResourceWithRawResponse(self._devices.deployment_groups)
+
@cached_property
def networks(self) -> AsyncNetworksResourceWithRawResponse:
return AsyncNetworksResourceWithRawResponse(self._devices.networks)
@@ -630,6 +654,10 @@ def dex_tests(self) -> DEXTestsResourceWithStreamingResponse:
def ip_profiles(self) -> IPProfilesResourceWithStreamingResponse:
return IPProfilesResourceWithStreamingResponse(self._devices.ip_profiles)
+ @cached_property
+ def deployment_groups(self) -> DeploymentGroupsResourceWithStreamingResponse:
+ return DeploymentGroupsResourceWithStreamingResponse(self._devices.deployment_groups)
+
@cached_property
def networks(self) -> NetworksResourceWithStreamingResponse:
return NetworksResourceWithStreamingResponse(self._devices.networks)
@@ -698,6 +726,10 @@ def dex_tests(self) -> AsyncDEXTestsResourceWithStreamingResponse:
def ip_profiles(self) -> AsyncIPProfilesResourceWithStreamingResponse:
return AsyncIPProfilesResourceWithStreamingResponse(self._devices.ip_profiles)
+ @cached_property
+ def deployment_groups(self) -> AsyncDeploymentGroupsResourceWithStreamingResponse:
+ return AsyncDeploymentGroupsResourceWithStreamingResponse(self._devices.deployment_groups)
+
@cached_property
def networks(self) -> AsyncNetworksResourceWithStreamingResponse:
return AsyncNetworksResourceWithStreamingResponse(self._devices.networks)
diff --git a/src/cloudflare/resources/zero_trust/identity_providers/scim/groups.py b/src/cloudflare/resources/zero_trust/identity_providers/scim/groups.py
index 4fdf8cb5bea..7bc9fa1f7d6 100644
--- a/src/cloudflare/resources/zero_trust/identity_providers/scim/groups.py
+++ b/src/cloudflare/resources/zero_trust/identity_providers/scim/groups.py
@@ -4,7 +4,7 @@
import httpx
-from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ....._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given
from ....._utils import path_template, maybe_transform
from ....._compat import cached_property
from ....._resource import SyncAPIResource, AsyncAPIResource
@@ -47,8 +47,8 @@ def list(
identity_provider_id: str,
*,
account_id: str,
- cf_resource_id: str | Omit = omit,
- idp_resource_id: str | Omit = omit,
+ cf_resource_id: SequenceNotStr[str] | Omit = omit,
+ idp_resource_id: SequenceNotStr[str] | Omit = omit,
name: str | Omit = omit,
page: int | Omit = omit,
per_page: int | Omit = omit,
@@ -69,10 +69,16 @@ def list(
identity_provider_id: UUID.
cf_resource_id: The unique Cloudflare-generated Id of the SCIM Group resource; also known as the
- "Id".
+ "Id". Pass once for a single lookup (`?cf_resource_id=A`) or repeat the
+ parameter (`?cf_resource_id=A&cf_resource_id=B`) to look up multiple groups in
+ one request, up to 50 values. Mutually exclusive with `idp_resource_id`, `name`,
+ `search_contains`, and `search_starts_with`.
idp_resource_id: The IdP-generated Id of the SCIM Group resource; also known as the "external
- Id".
+ Id". Pass once for a single lookup (`?idp_resource_id=A`) or repeat the
+ parameter (`?idp_resource_id=A&idp_resource_id=B`) to look up multiple groups in
+ one request, up to 50 values. Mutually exclusive with `cf_resource_id`, `name`,
+ `search_contains`, and `search_starts_with`.
name: The display name of the SCIM Group resource.
@@ -146,8 +152,8 @@ def list(
identity_provider_id: str,
*,
account_id: str,
- cf_resource_id: str | Omit = omit,
- idp_resource_id: str | Omit = omit,
+ cf_resource_id: SequenceNotStr[str] | Omit = omit,
+ idp_resource_id: SequenceNotStr[str] | Omit = omit,
name: str | Omit = omit,
page: int | Omit = omit,
per_page: int | Omit = omit,
@@ -168,10 +174,16 @@ def list(
identity_provider_id: UUID.
cf_resource_id: The unique Cloudflare-generated Id of the SCIM Group resource; also known as the
- "Id".
+ "Id". Pass once for a single lookup (`?cf_resource_id=A`) or repeat the
+ parameter (`?cf_resource_id=A&cf_resource_id=B`) to look up multiple groups in
+ one request, up to 50 values. Mutually exclusive with `idp_resource_id`, `name`,
+ `search_contains`, and `search_starts_with`.
idp_resource_id: The IdP-generated Id of the SCIM Group resource; also known as the "external
- Id".
+ Id". Pass once for a single lookup (`?idp_resource_id=A`) or repeat the
+ parameter (`?idp_resource_id=A&idp_resource_id=B`) to look up multiple groups in
+ one request, up to 50 values. Mutually exclusive with `cf_resource_id`, `name`,
+ `search_contains`, and `search_starts_with`.
name: The display name of the SCIM Group resource.
diff --git a/src/cloudflare/resources/zero_trust/identity_providers/scim/users.py b/src/cloudflare/resources/zero_trust/identity_providers/scim/users.py
index e746cce4ace..e2f59069361 100644
--- a/src/cloudflare/resources/zero_trust/identity_providers/scim/users.py
+++ b/src/cloudflare/resources/zero_trust/identity_providers/scim/users.py
@@ -4,7 +4,7 @@
import httpx
-from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ....._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given
from ....._utils import path_template, maybe_transform
from ....._compat import cached_property
from ....._resource import SyncAPIResource, AsyncAPIResource
@@ -47,9 +47,9 @@ def list(
identity_provider_id: str,
*,
account_id: str,
- cf_resource_id: str | Omit = omit,
+ cf_resource_id: SequenceNotStr[str] | Omit = omit,
email: str | Omit = omit,
- idp_resource_id: str | Omit = omit,
+ idp_resource_id: SequenceNotStr[str] | Omit = omit,
name: str | Omit = omit,
page: int | Omit = omit,
per_page: int | Omit = omit,
@@ -71,11 +71,18 @@ def list(
identity_provider_id: UUID.
cf_resource_id: The unique Cloudflare-generated Id of the SCIM User resource; also known as the
- "Id".
+ "Id". Pass once for a single lookup (`?cf_resource_id=A`) or repeat the
+ parameter (`?cf_resource_id=A&cf_resource_id=B`) to look up multiple users in
+ one request, up to 50 values. Mutually exclusive with `idp_resource_id`,
+ `username`, `email`, `name`, `search_contains`, and `search_starts_with`.
email: The email address of the SCIM User resource.
idp_resource_id: The IdP-generated Id of the SCIM User resource; also known as the "external Id".
+ Pass once for a single lookup (`?idp_resource_id=A`) or repeat the parameter
+ (`?idp_resource_id=A&idp_resource_id=B`) to look up multiple users in one
+ request, up to 50 values. Mutually exclusive with `cf_resource_id`, `username`,
+ `email`, `name`, `search_contains`, and `search_starts_with`.
name: The name of the SCIM User resource.
@@ -153,9 +160,9 @@ def list(
identity_provider_id: str,
*,
account_id: str,
- cf_resource_id: str | Omit = omit,
+ cf_resource_id: SequenceNotStr[str] | Omit = omit,
email: str | Omit = omit,
- idp_resource_id: str | Omit = omit,
+ idp_resource_id: SequenceNotStr[str] | Omit = omit,
name: str | Omit = omit,
page: int | Omit = omit,
per_page: int | Omit = omit,
@@ -177,11 +184,18 @@ def list(
identity_provider_id: UUID.
cf_resource_id: The unique Cloudflare-generated Id of the SCIM User resource; also known as the
- "Id".
+ "Id". Pass once for a single lookup (`?cf_resource_id=A`) or repeat the
+ parameter (`?cf_resource_id=A&cf_resource_id=B`) to look up multiple users in
+ one request, up to 50 values. Mutually exclusive with `idp_resource_id`,
+ `username`, `email`, `name`, `search_contains`, and `search_starts_with`.
email: The email address of the SCIM User resource.
idp_resource_id: The IdP-generated Id of the SCIM User resource; also known as the "external Id".
+ Pass once for a single lookup (`?idp_resource_id=A`) or repeat the parameter
+ (`?idp_resource_id=A&idp_resource_id=B`) to look up multiple users in one
+ request, up to 50 values. Mutually exclusive with `cf_resource_id`, `username`,
+ `email`, `name`, `search_contains`, and `search_starts_with`.
name: The name of the SCIM User resource.
diff --git a/src/cloudflare/resources/zones/zones.py b/src/cloudflare/resources/zones/zones.py
index 290011057ee..ceba8dfd843 100644
--- a/src/cloudflare/resources/zones/zones.py
+++ b/src/cloudflare/resources/zones/zones.py
@@ -2,7 +2,7 @@
from __future__ import annotations
-from typing import Type as TypingType, Optional, cast
+from typing import List, Type as TypingType, Optional, cast
from typing_extensions import Literal
import httpx
@@ -209,6 +209,7 @@ def list(
page: float | Omit = omit,
per_page: float | Omit = omit,
status: Literal["initializing", "pending", "active", "moved"] | Omit = omit,
+ type: List[Literal["full", "partial", "secondary", "internal"]] | 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,
@@ -246,6 +247,10 @@ def list(
status: Specify a zone status to filter by.
+ type: Zone types to filter by. Multiple types can be specified as a comma-separated
+ list (e.g., ?type=full,partial,secondary). When this parameter is not provided,
+ zones with type "internal" are excluded from the results.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -272,6 +277,7 @@ def list(
"page": page,
"per_page": per_page,
"status": status,
+ "type": type,
},
zone_list_params.ZoneListParams,
),
@@ -537,6 +543,7 @@ def list(
page: float | Omit = omit,
per_page: float | Omit = omit,
status: Literal["initializing", "pending", "active", "moved"] | Omit = omit,
+ type: List[Literal["full", "partial", "secondary", "internal"]] | 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,
@@ -574,6 +581,10 @@ def list(
status: Specify a zone status to filter by.
+ type: Zone types to filter by. Multiple types can be specified as a comma-separated
+ list (e.g., ?type=full,partial,secondary). When this parameter is not provided,
+ zones with type "internal" are excluded from the results.
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -600,6 +611,7 @@ def list(
"page": page,
"per_page": per_page,
"status": status,
+ "type": type,
},
zone_list_params.ZoneListParams,
),
diff --git a/src/cloudflare/types/aisearch/instance_create_params.py b/src/cloudflare/types/aisearch/instance_create_params.py
index 23246fed684..66b6ae9f39b 100644
--- a/src/cloudflare/types/aisearch/instance_create_params.py
+++ b/src/cloudflare/types/aisearch/instance_create_params.py
@@ -81,6 +81,13 @@ class InstanceCreateParams(TypedDict, total=False):
cache_threshold: Literal["super_strict_match", "close_enough", "flexible_friend", "anything_goes"]
+ cache_ttl: Literal[600, 1800, 3600, 7200, 21600, 43200, 86400, 172800, 259200, 518400]
+ """Cache entry TTL in seconds.
+
+ Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h),
+ 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).
+ """
+
chunk: bool
chunk_overlap: int
diff --git a/src/cloudflare/types/aisearch/instance_create_response.py b/src/cloudflare/types/aisearch/instance_create_response.py
index 9640f00c300..29f8aa3e5a1 100644
--- a/src/cloudflare/types/aisearch/instance_create_response.py
+++ b/src/cloudflare/types/aisearch/instance_create_response.py
@@ -282,6 +282,13 @@ class InstanceCreateResponse(BaseModel):
cache_threshold: Optional[Literal["super_strict_match", "close_enough", "flexible_friend", "anything_goes"]] = None
+ cache_ttl: Optional[Literal[600, 1800, 3600, 7200, 21600, 43200, 86400, 172800, 259200, 518400]] = None
+ """Cache entry TTL in seconds.
+
+ Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h),
+ 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).
+ """
+
chunk_overlap: Optional[int] = None
chunk_size: Optional[int] = None
diff --git a/src/cloudflare/types/aisearch/instance_delete_response.py b/src/cloudflare/types/aisearch/instance_delete_response.py
index d58c218ba08..63b7cc489f8 100644
--- a/src/cloudflare/types/aisearch/instance_delete_response.py
+++ b/src/cloudflare/types/aisearch/instance_delete_response.py
@@ -282,6 +282,13 @@ class InstanceDeleteResponse(BaseModel):
cache_threshold: Optional[Literal["super_strict_match", "close_enough", "flexible_friend", "anything_goes"]] = None
+ cache_ttl: Optional[Literal[600, 1800, 3600, 7200, 21600, 43200, 86400, 172800, 259200, 518400]] = None
+ """Cache entry TTL in seconds.
+
+ Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h),
+ 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).
+ """
+
chunk_overlap: Optional[int] = None
chunk_size: Optional[int] = None
diff --git a/src/cloudflare/types/aisearch/instance_list_response.py b/src/cloudflare/types/aisearch/instance_list_response.py
index 23678ce553e..725f969e08d 100644
--- a/src/cloudflare/types/aisearch/instance_list_response.py
+++ b/src/cloudflare/types/aisearch/instance_list_response.py
@@ -282,6 +282,13 @@ class InstanceListResponse(BaseModel):
cache_threshold: Optional[Literal["super_strict_match", "close_enough", "flexible_friend", "anything_goes"]] = None
+ cache_ttl: Optional[Literal[600, 1800, 3600, 7200, 21600, 43200, 86400, 172800, 259200, 518400]] = None
+ """Cache entry TTL in seconds.
+
+ Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h),
+ 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).
+ """
+
chunk_overlap: Optional[int] = None
chunk_size: Optional[int] = None
diff --git a/src/cloudflare/types/aisearch/instance_read_response.py b/src/cloudflare/types/aisearch/instance_read_response.py
index 45eaeae759e..dc0a8c18c72 100644
--- a/src/cloudflare/types/aisearch/instance_read_response.py
+++ b/src/cloudflare/types/aisearch/instance_read_response.py
@@ -282,6 +282,13 @@ class InstanceReadResponse(BaseModel):
cache_threshold: Optional[Literal["super_strict_match", "close_enough", "flexible_friend", "anything_goes"]] = None
+ cache_ttl: Optional[Literal[600, 1800, 3600, 7200, 21600, 43200, 86400, 172800, 259200, 518400]] = None
+ """Cache entry TTL in seconds.
+
+ Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h),
+ 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).
+ """
+
chunk_overlap: Optional[int] = None
chunk_size: Optional[int] = None
diff --git a/src/cloudflare/types/aisearch/instance_update_params.py b/src/cloudflare/types/aisearch/instance_update_params.py
index 549194bf577..fddda6864f0 100644
--- a/src/cloudflare/types/aisearch/instance_update_params.py
+++ b/src/cloudflare/types/aisearch/instance_update_params.py
@@ -78,6 +78,13 @@ class InstanceUpdateParams(TypedDict, total=False):
cache_threshold: Literal["super_strict_match", "close_enough", "flexible_friend", "anything_goes"]
+ cache_ttl: Literal[600, 1800, 3600, 7200, 21600, 43200, 86400, 172800, 259200, 518400]
+ """Cache entry TTL in seconds.
+
+ Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h),
+ 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).
+ """
+
chunk: bool
chunk_overlap: int
diff --git a/src/cloudflare/types/aisearch/instance_update_response.py b/src/cloudflare/types/aisearch/instance_update_response.py
index 5abc922e677..379e9d7b207 100644
--- a/src/cloudflare/types/aisearch/instance_update_response.py
+++ b/src/cloudflare/types/aisearch/instance_update_response.py
@@ -282,6 +282,13 @@ class InstanceUpdateResponse(BaseModel):
cache_threshold: Optional[Literal["super_strict_match", "close_enough", "flexible_friend", "anything_goes"]] = None
+ cache_ttl: Optional[Literal[600, 1800, 3600, 7200, 21600, 43200, 86400, 172800, 259200, 518400]] = None
+ """Cache entry TTL in seconds.
+
+ Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h),
+ 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).
+ """
+
chunk_overlap: Optional[int] = None
chunk_size: Optional[int] = None
diff --git a/src/cloudflare/types/aisearch/namespaces/instance_create_params.py b/src/cloudflare/types/aisearch/namespaces/instance_create_params.py
index d8f8a70a6cc..10493f5da1a 100644
--- a/src/cloudflare/types/aisearch/namespaces/instance_create_params.py
+++ b/src/cloudflare/types/aisearch/namespaces/instance_create_params.py
@@ -81,6 +81,13 @@ class InstanceCreateParams(TypedDict, total=False):
cache_threshold: Literal["super_strict_match", "close_enough", "flexible_friend", "anything_goes"]
+ cache_ttl: Literal[600, 1800, 3600, 7200, 21600, 43200, 86400, 172800, 259200, 518400]
+ """Cache entry TTL in seconds.
+
+ Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h),
+ 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).
+ """
+
chunk: bool
chunk_overlap: int
diff --git a/src/cloudflare/types/aisearch/namespaces/instance_create_response.py b/src/cloudflare/types/aisearch/namespaces/instance_create_response.py
index 0dee72d00d8..3e320dec61a 100644
--- a/src/cloudflare/types/aisearch/namespaces/instance_create_response.py
+++ b/src/cloudflare/types/aisearch/namespaces/instance_create_response.py
@@ -282,6 +282,13 @@ class InstanceCreateResponse(BaseModel):
cache_threshold: Optional[Literal["super_strict_match", "close_enough", "flexible_friend", "anything_goes"]] = None
+ cache_ttl: Optional[Literal[600, 1800, 3600, 7200, 21600, 43200, 86400, 172800, 259200, 518400]] = None
+ """Cache entry TTL in seconds.
+
+ Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h),
+ 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).
+ """
+
chunk_overlap: Optional[int] = None
chunk_size: Optional[int] = None
diff --git a/src/cloudflare/types/aisearch/namespaces/instance_delete_response.py b/src/cloudflare/types/aisearch/namespaces/instance_delete_response.py
index e870f6fc530..6caf390c7f4 100644
--- a/src/cloudflare/types/aisearch/namespaces/instance_delete_response.py
+++ b/src/cloudflare/types/aisearch/namespaces/instance_delete_response.py
@@ -282,6 +282,13 @@ class InstanceDeleteResponse(BaseModel):
cache_threshold: Optional[Literal["super_strict_match", "close_enough", "flexible_friend", "anything_goes"]] = None
+ cache_ttl: Optional[Literal[600, 1800, 3600, 7200, 21600, 43200, 86400, 172800, 259200, 518400]] = None
+ """Cache entry TTL in seconds.
+
+ Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h),
+ 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).
+ """
+
chunk_overlap: Optional[int] = None
chunk_size: Optional[int] = None
diff --git a/src/cloudflare/types/aisearch/namespaces/instance_list_response.py b/src/cloudflare/types/aisearch/namespaces/instance_list_response.py
index fa59d4b6eaa..70f2d02faf3 100644
--- a/src/cloudflare/types/aisearch/namespaces/instance_list_response.py
+++ b/src/cloudflare/types/aisearch/namespaces/instance_list_response.py
@@ -282,6 +282,13 @@ class InstanceListResponse(BaseModel):
cache_threshold: Optional[Literal["super_strict_match", "close_enough", "flexible_friend", "anything_goes"]] = None
+ cache_ttl: Optional[Literal[600, 1800, 3600, 7200, 21600, 43200, 86400, 172800, 259200, 518400]] = None
+ """Cache entry TTL in seconds.
+
+ Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h),
+ 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).
+ """
+
chunk_overlap: Optional[int] = None
chunk_size: Optional[int] = None
diff --git a/src/cloudflare/types/aisearch/namespaces/instance_read_response.py b/src/cloudflare/types/aisearch/namespaces/instance_read_response.py
index bf2763a2eeb..1c2e469c282 100644
--- a/src/cloudflare/types/aisearch/namespaces/instance_read_response.py
+++ b/src/cloudflare/types/aisearch/namespaces/instance_read_response.py
@@ -282,6 +282,13 @@ class InstanceReadResponse(BaseModel):
cache_threshold: Optional[Literal["super_strict_match", "close_enough", "flexible_friend", "anything_goes"]] = None
+ cache_ttl: Optional[Literal[600, 1800, 3600, 7200, 21600, 43200, 86400, 172800, 259200, 518400]] = None
+ """Cache entry TTL in seconds.
+
+ Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h),
+ 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).
+ """
+
chunk_overlap: Optional[int] = None
chunk_size: Optional[int] = None
diff --git a/src/cloudflare/types/aisearch/namespaces/instance_update_params.py b/src/cloudflare/types/aisearch/namespaces/instance_update_params.py
index 0834d5ab51c..057cd3d1241 100644
--- a/src/cloudflare/types/aisearch/namespaces/instance_update_params.py
+++ b/src/cloudflare/types/aisearch/namespaces/instance_update_params.py
@@ -80,6 +80,13 @@ class InstanceUpdateParams(TypedDict, total=False):
cache_threshold: Literal["super_strict_match", "close_enough", "flexible_friend", "anything_goes"]
+ cache_ttl: Literal[600, 1800, 3600, 7200, 21600, 43200, 86400, 172800, 259200, 518400]
+ """Cache entry TTL in seconds.
+
+ Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h),
+ 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).
+ """
+
chunk: bool
chunk_overlap: int
diff --git a/src/cloudflare/types/aisearch/namespaces/instance_update_response.py b/src/cloudflare/types/aisearch/namespaces/instance_update_response.py
index 6410208a0d7..7de705537a7 100644
--- a/src/cloudflare/types/aisearch/namespaces/instance_update_response.py
+++ b/src/cloudflare/types/aisearch/namespaces/instance_update_response.py
@@ -282,6 +282,13 @@ class InstanceUpdateResponse(BaseModel):
cache_threshold: Optional[Literal["super_strict_match", "close_enough", "flexible_friend", "anything_goes"]] = None
+ cache_ttl: Optional[Literal[600, 1800, 3600, 7200, 21600, 43200, 86400, 172800, 259200, 518400]] = None
+ """Cache entry TTL in seconds.
+
+ Allowed values: 600 (10min), 1800 (30min), 3600 (1h), 7200 (2h), 21600 (6h),
+ 43200 (12h), 86400 (24h), 172800 (48h), 259200 (72h), 518400 (6d).
+ """
+
chunk_overlap: Optional[int] = None
chunk_size: Optional[int] = None
diff --git a/src/cloudflare/types/email_security/submission_list_params.py b/src/cloudflare/types/email_security/submission_list_params.py
index b333cbd18b4..05b4f85a5c1 100644
--- a/src/cloudflare/types/email_security/submission_list_params.py
+++ b/src/cloudflare/types/email_security/submission_list_params.py
@@ -18,6 +18,13 @@ class SubmissionListParams(TypedDict, total=False):
end: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")]
"""The end of the search date range. Defaults to `now`."""
+ escalated_from_user: bool
+ """When true, return only submissions that were escalated by an end user (vs.
+
+ by the security team). When false, return only submissions that were not
+ escalated by an end user. When omitted, no filter is applied.
+ """
+
original_disposition: Literal["MALICIOUS", "SUSPICIOUS", "SPOOF", "SPAM", "BULK", "NONE"]
outcome_disposition: Literal["MALICIOUS", "SUSPICIOUS", "SPOOF", "SPAM", "BULK", "NONE"]
diff --git a/src/cloudflare/types/radar/dns_timeseries_groups_v2_params.py b/src/cloudflare/types/radar/dns_timeseries_groups_v2_params.py
index 34262ad0cdd..4afa74b1e66 100644
--- a/src/cloudflare/types/radar/dns_timeseries_groups_v2_params.py
+++ b/src/cloudflare/types/radar/dns_timeseries_groups_v2_params.py
@@ -94,7 +94,7 @@ class DNSTimeseriesGroupsV2Params(TypedDict, total=False):
nodata: Iterable[bool]
"""Specifies whether the response includes empty DNS responses (NODATA)."""
- normalization: Literal["PERCENTAGE", "MIN0_MAX"]
+ normalization: Literal["PERCENTAGE", "MIN0_MAX", "RANK"]
"""Normalization method applied to the results.
Refer to
diff --git a/src/cloudflare/types/security_center/insights/__init__.py b/src/cloudflare/types/security_center/insights/__init__.py
index adb9484cde5..08a3073ded2 100644
--- a/src/cloudflare/types/security_center/insights/__init__.py
+++ b/src/cloudflare/types/security_center/insights/__init__.py
@@ -7,4 +7,11 @@
from .type_get_response import TypeGetResponse as TypeGetResponse
from .class_get_response import ClassGetResponse as ClassGetResponse
from .severity_get_params import SeverityGetParams as SeverityGetParams
+from .context_get_response import ContextGetResponse as ContextGetResponse
+from .audit_log_list_params import AuditLogListParams as AuditLogListParams
from .severity_get_response import SeverityGetResponse as SeverityGetResponse
+from .audit_log_list_response import AuditLogListResponse as AuditLogListResponse
+from .classification_update_params import ClassificationUpdateParams as ClassificationUpdateParams
+from .classification_update_response import ClassificationUpdateResponse as ClassificationUpdateResponse
+from .audit_log_list_by_insight_params import AuditLogListByInsightParams as AuditLogListByInsightParams
+from .audit_log_list_by_insight_response import AuditLogListByInsightResponse as AuditLogListByInsightResponse
diff --git a/src/cloudflare/types/security_center/insights/audit_log_list_by_insight_params.py b/src/cloudflare/types/security_center/insights/audit_log_list_by_insight_params.py
new file mode 100644
index 00000000000..7809ec6bcc3
--- /dev/null
+++ b/src/cloudflare/types/security_center/insights/audit_log_list_by_insight_params.py
@@ -0,0 +1,43 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union
+from datetime import datetime
+from typing_extensions import Literal, Annotated, TypedDict
+
+from ...._utils import PropertyInfo
+
+__all__ = ["AuditLogListByInsightParams"]
+
+
+class AuditLogListByInsightParams(TypedDict, total=False):
+ account_id: str
+ """The Account ID to use for this endpoint. Mutually exclusive with the Zone ID."""
+
+ zone_id: str
+ """The Zone ID to use for this endpoint. Mutually exclusive with the Account ID."""
+
+ before: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")]
+ """Filter entries changed before this timestamp (RFC 3339)."""
+
+ changed_by: str
+ """Filter by the actor that made the change."""
+
+ cursor: str
+ """Opaque cursor for pagination.
+
+ Use the cursor value from result_info of the previous response.
+ """
+
+ field_changed: Literal["status", "user_classification"]
+ """Filter by the field that was changed."""
+
+ order: Literal["asc", "desc"]
+ """Sort order for results. Use 'asc' for oldest first or 'desc' for newest first."""
+
+ per_page: int
+ """Number of results per page."""
+
+ since: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")]
+ """Filter entries changed at or after this timestamp (RFC 3339)."""
diff --git a/src/cloudflare/types/security_center/insights/audit_log_list_by_insight_response.py b/src/cloudflare/types/security_center/insights/audit_log_list_by_insight_response.py
new file mode 100644
index 00000000000..2439e24e31f
--- /dev/null
+++ b/src/cloudflare/types/security_center/insights/audit_log_list_by_insight_response.py
@@ -0,0 +1,44 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from ...._models import BaseModel
+
+__all__ = ["AuditLogListByInsightResponse"]
+
+
+class AuditLogListByInsightResponse(BaseModel):
+ id: Optional[str] = None
+ """UUIDv7 identifier for the audit log entry, time-ordered."""
+
+ changed_at: Optional[datetime] = None
+ """The timestamp when the change occurred."""
+
+ changed_by: Optional[str] = None
+ """The actor that made the change.
+
+ 'system' for automated changes, or a user identifier.
+ """
+
+ current_value: Optional[str] = None
+ """The value of the field after the change. Null if the field was cleared."""
+
+ field_changed: Optional[Literal["status", "user_classification"]] = None
+ """The field that was changed."""
+
+ issue_id: Optional[str] = None
+ """The ID of the insight this audit log entry relates to."""
+
+ previous_value: Optional[str] = None
+ """The value of the field before the change.
+
+ Null if the field was not previously set.
+ """
+
+ rationale: Optional[str] = None
+ """Optional rationale provided for the change."""
+
+ zone_id: Optional[int] = None
+ """The zone ID associated with the insight. Only present for zone-level insights."""
diff --git a/src/cloudflare/types/security_center/insights/audit_log_list_params.py b/src/cloudflare/types/security_center/insights/audit_log_list_params.py
new file mode 100644
index 00000000000..74e03879e2e
--- /dev/null
+++ b/src/cloudflare/types/security_center/insights/audit_log_list_params.py
@@ -0,0 +1,43 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union
+from datetime import datetime
+from typing_extensions import Literal, Annotated, TypedDict
+
+from ...._utils import PropertyInfo
+
+__all__ = ["AuditLogListParams"]
+
+
+class AuditLogListParams(TypedDict, total=False):
+ account_id: str
+ """The Account ID to use for this endpoint. Mutually exclusive with the Zone ID."""
+
+ zone_id: str
+ """The Zone ID to use for this endpoint. Mutually exclusive with the Account ID."""
+
+ before: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")]
+ """Filter entries changed before this timestamp (RFC 3339)."""
+
+ changed_by: str
+ """Filter by the actor that made the change."""
+
+ cursor: str
+ """Opaque cursor for pagination.
+
+ Use the cursor value from result_info of the previous response.
+ """
+
+ field_changed: Literal["status", "user_classification"]
+ """Filter by the field that was changed."""
+
+ order: Literal["asc", "desc"]
+ """Sort order for results. Use 'asc' for oldest first or 'desc' for newest first."""
+
+ per_page: int
+ """Number of results per page."""
+
+ since: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")]
+ """Filter entries changed at or after this timestamp (RFC 3339)."""
diff --git a/src/cloudflare/types/security_center/insights/audit_log_list_response.py b/src/cloudflare/types/security_center/insights/audit_log_list_response.py
new file mode 100644
index 00000000000..6bc3ac1ffc6
--- /dev/null
+++ b/src/cloudflare/types/security_center/insights/audit_log_list_response.py
@@ -0,0 +1,44 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from ...._models import BaseModel
+
+__all__ = ["AuditLogListResponse"]
+
+
+class AuditLogListResponse(BaseModel):
+ id: Optional[str] = None
+ """UUIDv7 identifier for the audit log entry, time-ordered."""
+
+ changed_at: Optional[datetime] = None
+ """The timestamp when the change occurred."""
+
+ changed_by: Optional[str] = None
+ """The actor that made the change.
+
+ 'system' for automated changes, or a user identifier.
+ """
+
+ current_value: Optional[str] = None
+ """The value of the field after the change. Null if the field was cleared."""
+
+ field_changed: Optional[Literal["status", "user_classification"]] = None
+ """The field that was changed."""
+
+ issue_id: Optional[str] = None
+ """The ID of the insight this audit log entry relates to."""
+
+ previous_value: Optional[str] = None
+ """The value of the field before the change.
+
+ Null if the field was not previously set.
+ """
+
+ rationale: Optional[str] = None
+ """Optional rationale provided for the change."""
+
+ zone_id: Optional[int] = None
+ """The zone ID associated with the insight. Only present for zone-level insights."""
diff --git a/src/cloudflare/types/security_center/insights/classification_update_params.py b/src/cloudflare/types/security_center/insights/classification_update_params.py
new file mode 100644
index 00000000000..3575bc2ec72
--- /dev/null
+++ b/src/cloudflare/types/security_center/insights/classification_update_params.py
@@ -0,0 +1,28 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal, TypedDict
+
+__all__ = ["ClassificationUpdateParams"]
+
+
+class ClassificationUpdateParams(TypedDict, total=False):
+ account_id: str
+ """The Account ID to use for this endpoint. Mutually exclusive with the Zone ID."""
+
+ zone_id: str
+ """The Zone ID to use for this endpoint. Mutually exclusive with the Account ID."""
+
+ classification: Optional[Literal["false_positive", "accept_risk", "other"]]
+ """User-defined classification for the insight.
+
+ Can be 'false_positive', 'accept_risk', 'other', or null.
+ """
+
+ rationale: str
+ """Rationale for the classification change.
+
+ Required when classification is 'accept_risk' or 'other'.
+ """
diff --git a/src/cloudflare/types/security_center/insights/classification_update_response.py b/src/cloudflare/types/security_center/insights/classification_update_response.py
new file mode 100644
index 00000000000..6718a2732f1
--- /dev/null
+++ b/src/cloudflare/types/security_center/insights/classification_update_response.py
@@ -0,0 +1,45 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import Literal
+
+from ...._models import BaseModel
+
+__all__ = ["ClassificationUpdateResponse", "Error", "ErrorSource", "Message", "MessageSource"]
+
+
+class ErrorSource(BaseModel):
+ pointer: Optional[str] = None
+
+
+class Error(BaseModel):
+ code: int
+
+ message: str
+
+ documentation_url: Optional[str] = None
+
+ source: Optional[ErrorSource] = None
+
+
+class MessageSource(BaseModel):
+ pointer: Optional[str] = None
+
+
+class Message(BaseModel):
+ code: int
+
+ message: str
+
+ documentation_url: Optional[str] = None
+
+ source: Optional[MessageSource] = None
+
+
+class ClassificationUpdateResponse(BaseModel):
+ errors: List[Error]
+
+ messages: List[Message]
+
+ success: Literal[True]
+ """Whether the API call was successful."""
diff --git a/src/cloudflare/types/security_center/insights/context_get_response.py b/src/cloudflare/types/security_center/insights/context_get_response.py
new file mode 100644
index 00000000000..af39d73b946
--- /dev/null
+++ b/src/cloudflare/types/security_center/insights/context_get_response.py
@@ -0,0 +1,8 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict
+from typing_extensions import TypeAlias
+
+__all__ = ["ContextGetResponse"]
+
+ContextGetResponse: TypeAlias = Dict[str, object]
diff --git a/src/cloudflare/types/zero_trust/access/logs/access_request_list_params.py b/src/cloudflare/types/zero_trust/access/logs/access_request_list_params.py
index c22b8ff0793..93d82bced7e 100644
--- a/src/cloudflare/types/zero_trust/access/logs/access_request_list_params.py
+++ b/src/cloudflare/types/zero_trust/access/logs/access_request_list_params.py
@@ -33,17 +33,28 @@ class AccessRequestListParams(TypedDict, total=False):
email: str
"""Filter by user email.
- Defaults to substring matching. To force exact matching, set `email_exact=true`.
- Example (default): `email=@example.com` returns all events with that domain.
- Example (exact): `email=user@example.com&email_exact=true` returns only that
- user.
+ Match mode is controlled by `emailOp` (preferred) or the legacy `email_exact`
+ flag.
+
+ - Default (no `emailOp`, `email_exact=false` or unset): substring match —
+ `email=@example.com` returns all events with that domain.
+ - Exact match: set `emailOp=eq` (preferred) or `email_exact=true` — e.g.
+ `email=user@example.com&email_exact=true` returns only that user.
+ - Explicit substring match: set `emailOp=contains` (without `email_exact=true`).
+ When both are set, `email_exact=true` takes precedence and the match is exact.
+ - Exclusion: set `emailOp=neq`. With `email_exact=true` this is an exact-value
+ exclusion; without it, a fuzzy substring exclusion.
"""
email_exact: bool
"""When true, `email` is matched exactly instead of substring matching."""
- email_op: Annotated[Literal["eq", "neq"], PropertyInfo(alias="emailOp")]
- """Operator for the `email` filter."""
+ email_op: Annotated[Literal["eq", "neq", "contains"], PropertyInfo(alias="emailOp")]
+ """
+ Operator for the `email` filter. `contains` performs a substring
+ (case-sensitive) match. When `email_exact=true` is also set, `email_exact` takes
+ precedence and `contains` is ignored.
+ """
fields: str
"""
@@ -76,7 +87,15 @@ class AccessRequestListParams(TypedDict, total=False):
"""The latest event timestamp to query."""
user_id: str
- """Filter by user UUID."""
+ """Deprecated.
+
+ Accepted for backward compatibility but no longer applied as a filter. Use
+ `email` instead.
+ """
user_id_op: Annotated[Literal["eq", "neq"], PropertyInfo(alias="user_idOp")]
- """Operator for the `user_id` filter."""
+ """Deprecated.
+
+ Accepted for backward compatibility but no longer applied as a filter (the
+ `user_id` parameter is itself deprecated).
+ """
diff --git a/src/cloudflare/types/zero_trust/access/logs/scim/update_list_params.py b/src/cloudflare/types/zero_trust/access/logs/scim/update_list_params.py
index 6ef5bb55bb5..f5dce87349e 100644
--- a/src/cloudflare/types/zero_trust/access/logs/scim/update_list_params.py
+++ b/src/cloudflare/types/zero_trust/access/logs/scim/update_list_params.py
@@ -19,14 +19,24 @@ class UpdateListParams(TypedDict, total=False):
idp_id: Required[SequenceNotStr[str]]
"""The unique Id of the IdP that has SCIM enabled."""
- cf_resource_id: str
- """The unique Cloudflare-generated Id of the SCIM resource."""
+ cf_resource_id: SequenceNotStr[str]
+ """The unique Cloudflare-generated Id of the SCIM resource.
+
+ Pass once for a single lookup (`?cf_resource_id=A`) or repeat the parameter
+ (`?cf_resource_id=A&cf_resource_id=B`) to filter by multiple resources in one
+ request.
+ """
direction: Literal["desc", "asc"]
"""The chronological order used to sort the logs."""
- idp_resource_id: str
- """The IdP-generated Id of the SCIM resource."""
+ idp_resource_id: SequenceNotStr[str]
+ """The IdP-generated Id of the SCIM resource.
+
+ Pass once for a single lookup (`?idp_resource_id=A`) or repeat the parameter
+ (`?idp_resource_id=A&idp_resource_id=B`) to filter by multiple resources in one
+ request.
+ """
limit: int
"""The maximum number of update logs to retrieve."""
@@ -40,14 +50,24 @@ class UpdateListParams(TypedDict, total=False):
request_method: List[Literal["DELETE", "PATCH", "POST", "PUT"]]
"""The request method of the SCIM request."""
- resource_group_name: str
- """The display name of the SCIM Group resource."""
+ resource_group_name: SequenceNotStr[str]
+ """The display name of the SCIM Group resource.
+
+ Pass once for a single lookup (`?resource_group_name=A`) or repeat the parameter
+ (`?resource_group_name=A&resource_group_name=B`) to filter by multiple group
+ names in one request.
+ """
resource_type: List[Literal["USER", "GROUP"]]
"""The resource type of the SCIM request."""
- resource_user_email: str
- """The email address of the SCIM User resource."""
+ resource_user_email: SequenceNotStr[str]
+ """The email address of the SCIM User resource.
+
+ Pass once for a single lookup (`?resource_user_email=A`) or repeat the parameter
+ (`?resource_user_email=A&resource_user_email=B`) to filter by multiple emails in
+ one request.
+ """
since: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")]
"""the timestamp of the earliest update log."""
diff --git a/src/cloudflare/types/zero_trust/devices/__init__.py b/src/cloudflare/types/zero_trust/devices/__init__.py
index b8a661717f2..55f3b9bb218 100644
--- a/src/cloudflare/types/zero_trust/devices/__init__.py
+++ b/src/cloudflare/types/zero_trust/devices/__init__.py
@@ -14,6 +14,7 @@
from .device_settings import DeviceSettings as DeviceSettings
from .fallback_domain import FallbackDomain as FallbackDomain
from .settings_policy import SettingsPolicy as SettingsPolicy
+from .deployment_group import DeploymentGroup as DeploymentGroup
from .file_input_param import FileInputParam as FileInputParam
from .os_version_input import OSVersionInput as OSVersionInput
from .carbonblack_input import CarbonblackInput as CarbonblackInput
@@ -80,7 +81,11 @@
from .split_tunnel_include_param import SplitTunnelIncludeParam as SplitTunnelIncludeParam
from .disk_encryption_input_param import DiskEncryptionInputParam as DiskEncryptionInputParam
from .sentinelone_s2s_input_param import SentineloneS2sInputParam as SentineloneS2sInputParam
+from .deployment_group_edit_params import DeploymentGroupEditParams as DeploymentGroupEditParams
+from .deployment_group_list_params import DeploymentGroupListParams as DeploymentGroupListParams
from .registration_unrevoke_params import RegistrationUnrevokeParams as RegistrationUnrevokeParams
from .unique_client_id_input_param import UniqueClientIDInputParam as UniqueClientIDInputParam
from .client_certificate_input_param import ClientCertificateInputParam as ClientCertificateInputParam
+from .deployment_group_create_params import DeploymentGroupCreateParams as DeploymentGroupCreateParams
from .registration_bulk_delete_params import RegistrationBulkDeleteParams as RegistrationBulkDeleteParams
+from .deployment_group_delete_response import DeploymentGroupDeleteResponse as DeploymentGroupDeleteResponse
diff --git a/src/cloudflare/types/zero_trust/devices/deployment_group.py b/src/cloudflare/types/zero_trust/devices/deployment_group.py
new file mode 100644
index 00000000000..e85073b3be9
--- /dev/null
+++ b/src/cloudflare/types/zero_trust/devices/deployment_group.py
@@ -0,0 +1,35 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+
+from ...._models import BaseModel
+
+__all__ = ["DeploymentGroup", "VersionConfig"]
+
+
+class VersionConfig(BaseModel):
+ target_environment: Optional[str] = None
+ """The target environment for the client version (e.g., windows, macos)."""
+
+ version: str
+ """The specific client version to deploy."""
+
+
+class DeploymentGroup(BaseModel):
+ id: str
+ """The ID of the deployment group."""
+
+ created_at: str
+ """The RFC3339Nano timestamp when the deployment group was created."""
+
+ name: str
+ """A user-friendly name for the deployment group."""
+
+ updated_at: str
+ """The RFC3339Nano timestamp when the deployment group was last updated."""
+
+ version_config: List[VersionConfig]
+ """Contains version configurations for different target environments."""
+
+ policy_ids: Optional[List[str]] = None
+ """Contains a list of policy IDs assigned to this deployment group."""
diff --git a/src/cloudflare/types/zero_trust/devices/deployment_group_create_params.py b/src/cloudflare/types/zero_trust/devices/deployment_group_create_params.py
new file mode 100644
index 00000000000..3cacec4ffe9
--- /dev/null
+++ b/src/cloudflare/types/zero_trust/devices/deployment_group_create_params.py
@@ -0,0 +1,31 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Iterable, Optional
+from typing_extensions import Required, TypedDict
+
+from ...._types import SequenceNotStr
+
+__all__ = ["DeploymentGroupCreateParams", "VersionConfig"]
+
+
+class DeploymentGroupCreateParams(TypedDict, total=False):
+ account_id: Required[str]
+
+ name: Required[str]
+ """A user-friendly name for the deployment group."""
+
+ version_config: Required[Iterable[VersionConfig]]
+ """Contains at least one version configuration."""
+
+ policy_ids: SequenceNotStr[str]
+ """Contains an optional list of policy IDs assigned to a group."""
+
+
+class VersionConfig(TypedDict, total=False):
+ target_environment: Required[Optional[str]]
+ """The target environment for the client version (e.g., windows, macos)."""
+
+ version: Required[str]
+ """The specific client version to deploy."""
diff --git a/src/cloudflare/types/zero_trust/devices/deployment_group_delete_response.py b/src/cloudflare/types/zero_trust/devices/deployment_group_delete_response.py
new file mode 100644
index 00000000000..39c4b672b0f
--- /dev/null
+++ b/src/cloudflare/types/zero_trust/devices/deployment_group_delete_response.py
@@ -0,0 +1,12 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from ...._models import BaseModel
+
+__all__ = ["DeploymentGroupDeleteResponse"]
+
+
+class DeploymentGroupDeleteResponse(BaseModel):
+ id: Optional[str] = None
+ """The ID of a deleted deployment group."""
diff --git a/src/cloudflare/types/zero_trust/devices/deployment_group_edit_params.py b/src/cloudflare/types/zero_trust/devices/deployment_group_edit_params.py
new file mode 100644
index 00000000000..843dab242f1
--- /dev/null
+++ b/src/cloudflare/types/zero_trust/devices/deployment_group_edit_params.py
@@ -0,0 +1,31 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Iterable, Optional
+from typing_extensions import Required, TypedDict
+
+from ...._types import SequenceNotStr
+
+__all__ = ["DeploymentGroupEditParams", "VersionConfig"]
+
+
+class DeploymentGroupEditParams(TypedDict, total=False):
+ account_id: Required[str]
+
+ name: str
+ """A user-friendly name for the deployment group."""
+
+ policy_ids: SequenceNotStr[str]
+ """Replaces the entire list of policy IDs."""
+
+ version_config: Iterable[VersionConfig]
+ """Replaces the entire version_config array."""
+
+
+class VersionConfig(TypedDict, total=False):
+ target_environment: Required[Optional[str]]
+ """The target environment for the client version (e.g., windows, macos)."""
+
+ version: Required[str]
+ """The specific client version to deploy."""
diff --git a/src/cloudflare/types/zero_trust/devices/deployment_group_list_params.py b/src/cloudflare/types/zero_trust/devices/deployment_group_list_params.py
new file mode 100644
index 00000000000..4c0295b4169
--- /dev/null
+++ b/src/cloudflare/types/zero_trust/devices/deployment_group_list_params.py
@@ -0,0 +1,17 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["DeploymentGroupListParams"]
+
+
+class DeploymentGroupListParams(TypedDict, total=False):
+ account_id: Required[str]
+
+ page: int
+ """The page number to return."""
+
+ per_page: int
+ """The maximum number of deployment groups to return per page."""
diff --git a/src/cloudflare/types/zero_trust/identity_providers/scim/group_list_params.py b/src/cloudflare/types/zero_trust/identity_providers/scim/group_list_params.py
index c94b4b175b1..370c558493a 100644
--- a/src/cloudflare/types/zero_trust/identity_providers/scim/group_list_params.py
+++ b/src/cloudflare/types/zero_trust/identity_providers/scim/group_list_params.py
@@ -4,6 +4,8 @@
from typing_extensions import Required, TypedDict
+from ....._types import SequenceNotStr
+
__all__ = ["GroupListParams"]
@@ -11,16 +13,22 @@ class GroupListParams(TypedDict, total=False):
account_id: Required[str]
"""Identifier."""
- cf_resource_id: str
+ cf_resource_id: SequenceNotStr[str]
"""
The unique Cloudflare-generated Id of the SCIM Group resource; also known as the
- "Id".
+ "Id". Pass once for a single lookup (`?cf_resource_id=A`) or repeat the
+ parameter (`?cf_resource_id=A&cf_resource_id=B`) to look up multiple groups in
+ one request, up to 50 values. Mutually exclusive with `idp_resource_id`, `name`,
+ `search_contains`, and `search_starts_with`.
"""
- idp_resource_id: str
+ idp_resource_id: SequenceNotStr[str]
"""
The IdP-generated Id of the SCIM Group resource; also known as the "external
- Id".
+ Id". Pass once for a single lookup (`?idp_resource_id=A`) or repeat the
+ parameter (`?idp_resource_id=A&idp_resource_id=B`) to look up multiple groups in
+ one request, up to 50 values. Mutually exclusive with `cf_resource_id`, `name`,
+ `search_contains`, and `search_starts_with`.
"""
name: str
diff --git a/src/cloudflare/types/zero_trust/identity_providers/scim/user_list_params.py b/src/cloudflare/types/zero_trust/identity_providers/scim/user_list_params.py
index 6ce2445eec1..f87694d9bd9 100644
--- a/src/cloudflare/types/zero_trust/identity_providers/scim/user_list_params.py
+++ b/src/cloudflare/types/zero_trust/identity_providers/scim/user_list_params.py
@@ -4,6 +4,8 @@
from typing_extensions import Required, TypedDict
+from ....._types import SequenceNotStr
+
__all__ = ["UserListParams"]
@@ -11,18 +13,25 @@ class UserListParams(TypedDict, total=False):
account_id: Required[str]
"""Identifier."""
- cf_resource_id: str
+ cf_resource_id: SequenceNotStr[str]
"""
The unique Cloudflare-generated Id of the SCIM User resource; also known as the
- "Id".
+ "Id". Pass once for a single lookup (`?cf_resource_id=A`) or repeat the
+ parameter (`?cf_resource_id=A&cf_resource_id=B`) to look up multiple users in
+ one request, up to 50 values. Mutually exclusive with `idp_resource_id`,
+ `username`, `email`, `name`, `search_contains`, and `search_starts_with`.
"""
email: str
"""The email address of the SCIM User resource."""
- idp_resource_id: str
+ idp_resource_id: SequenceNotStr[str]
"""
The IdP-generated Id of the SCIM User resource; also known as the "external Id".
+ Pass once for a single lookup (`?idp_resource_id=A`) or repeat the parameter
+ (`?idp_resource_id=A&idp_resource_id=B`) to look up multiple users in one
+ request, up to 50 values. Mutually exclusive with `cf_resource_id`, `username`,
+ `email`, `name`, `search_contains`, and `search_starts_with`.
"""
name: str
diff --git a/src/cloudflare/types/zones/setting_edit_response.py b/src/cloudflare/types/zones/setting_edit_response.py
index 9fd2e3ef777..e1107566376 100644
--- a/src/cloudflare/types/zones/setting_edit_response.py
+++ b/src/cloudflare/types/zones/setting_edit_response.py
@@ -63,6 +63,7 @@
"ZonesSchemasResponseBuffering",
"ZonesSchemasRocketLoader",
"ZonesSchemasAutomaticPlatformOptimization",
+ "ZonesSearchForAgents",
"ZonesSchemasSecurityLevel",
"ZonesSha1Support",
"ZonesSchemasSortQueryStringForCache",
@@ -616,6 +617,30 @@ class ZonesSchemasAutomaticPlatformOptimization(BaseModel):
"""last time this setting was modified."""
+class ZonesSearchForAgents(BaseModel):
+ """
+ When enabled, Cloudflare provisions an AI Search instance for the zone
+ and exposes a /.well-known/ai-search endpoint that AI agents can query.
+ Markdown responses also receive an agent: YAML capability block advertising
+ the search endpoint.
+ """
+
+ id: Literal["search_for_agents"]
+ """ID of the zone setting."""
+
+ value: Literal["off", "on"]
+ """Current value of the zone setting."""
+
+ editable: Optional[Literal[True, False]] = None
+ """
+ Whether or not this setting can be modified for this zone (based on your
+ Cloudflare plan level).
+ """
+
+ modified_on: Optional[datetime] = None
+ """last time this setting was modified."""
+
+
class ZonesSchemasSecurityLevel(BaseModel):
"""
Choose the appropriate security profile for your website, which will automatically adjust each of the security settings. If you choose to customize an individual security setting, the profile will become Custom. (https://support.cloudflare.com/hc/en-us/articles/200170056).
@@ -855,6 +880,7 @@ class ZonesSchemasWAF(BaseModel):
ZonesSchemasResponseBuffering,
ZonesSchemasRocketLoader,
ZonesSchemasAutomaticPlatformOptimization,
+ ZonesSearchForAgents,
SecurityHeaders,
ZonesSchemasSecurityLevel,
ServerSideExcludes,
diff --git a/src/cloudflare/types/zones/setting_get_response.py b/src/cloudflare/types/zones/setting_get_response.py
index ac20c206eb1..3b66fbf3293 100644
--- a/src/cloudflare/types/zones/setting_get_response.py
+++ b/src/cloudflare/types/zones/setting_get_response.py
@@ -63,6 +63,7 @@
"ZonesSchemasResponseBuffering",
"ZonesSchemasRocketLoader",
"ZonesSchemasAutomaticPlatformOptimization",
+ "ZonesSearchForAgents",
"ZonesSchemasSecurityLevel",
"ZonesSha1Support",
"ZonesSchemasSortQueryStringForCache",
@@ -616,6 +617,30 @@ class ZonesSchemasAutomaticPlatformOptimization(BaseModel):
"""last time this setting was modified."""
+class ZonesSearchForAgents(BaseModel):
+ """
+ When enabled, Cloudflare provisions an AI Search instance for the zone
+ and exposes a /.well-known/ai-search endpoint that AI agents can query.
+ Markdown responses also receive an agent: YAML capability block advertising
+ the search endpoint.
+ """
+
+ id: Literal["search_for_agents"]
+ """ID of the zone setting."""
+
+ value: Literal["off", "on"]
+ """Current value of the zone setting."""
+
+ editable: Optional[Literal[True, False]] = None
+ """
+ Whether or not this setting can be modified for this zone (based on your
+ Cloudflare plan level).
+ """
+
+ modified_on: Optional[datetime] = None
+ """last time this setting was modified."""
+
+
class ZonesSchemasSecurityLevel(BaseModel):
"""
Choose the appropriate security profile for your website, which will automatically adjust each of the security settings. If you choose to customize an individual security setting, the profile will become Custom. (https://support.cloudflare.com/hc/en-us/articles/200170056).
@@ -855,6 +880,7 @@ class ZonesSchemasWAF(BaseModel):
ZonesSchemasResponseBuffering,
ZonesSchemasRocketLoader,
ZonesSchemasAutomaticPlatformOptimization,
+ ZonesSearchForAgents,
SecurityHeaders,
ZonesSchemasSecurityLevel,
ServerSideExcludes,
diff --git a/src/cloudflare/types/zones/zone_list_params.py b/src/cloudflare/types/zones/zone_list_params.py
index 8e6985ebfe1..706077d2171 100644
--- a/src/cloudflare/types/zones/zone_list_params.py
+++ b/src/cloudflare/types/zones/zone_list_params.py
@@ -2,6 +2,7 @@
from __future__ import annotations
+from typing import List
from typing_extensions import Literal, TypedDict
__all__ = ["ZoneListParams", "Account"]
@@ -43,6 +44,14 @@ class ZoneListParams(TypedDict, total=False):
status: Literal["initializing", "pending", "active", "moved"]
"""Specify a zone status to filter by."""
+ type: List[Literal["full", "partial", "secondary", "internal"]]
+ """Zone types to filter by.
+
+ Multiple types can be specified as a comma-separated list (e.g.,
+ ?type=full,partial,secondary). When this parameter is not provided, zones with
+ type "internal" are excluded from the results.
+ """
+
class Account(TypedDict, total=False):
id: str
diff --git a/tests/api_resources/aisearch/namespaces/test_instances.py b/tests/api_resources/aisearch/namespaces/test_instances.py
index ceaff00d4be..4190bfaa776 100644
--- a/tests/api_resources/aisearch/namespaces/test_instances.py
+++ b/tests/api_resources/aisearch/namespaces/test_instances.py
@@ -46,6 +46,7 @@ def test_method_create_with_all_params(self, client: Cloudflare) -> None:
aisearch_model="@cf/meta/llama-3.3-70b-instruct-fp8-fast",
cache=True,
cache_threshold="super_strict_match",
+ cache_ttl=600,
chunk=True,
chunk_overlap=0,
chunk_size=64,
@@ -203,6 +204,7 @@ def test_method_update_with_all_params(self, client: Cloudflare) -> None:
aisearch_model="@cf/meta/llama-3.3-70b-instruct-fp8-fast",
cache=True,
cache_threshold="super_strict_match",
+ cache_ttl=600,
chunk=True,
chunk_overlap=0,
chunk_size=64,
@@ -871,6 +873,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncCloudflare
aisearch_model="@cf/meta/llama-3.3-70b-instruct-fp8-fast",
cache=True,
cache_threshold="super_strict_match",
+ cache_ttl=600,
chunk=True,
chunk_overlap=0,
chunk_size=64,
@@ -1028,6 +1031,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncCloudflare
aisearch_model="@cf/meta/llama-3.3-70b-instruct-fp8-fast",
cache=True,
cache_threshold="super_strict_match",
+ cache_ttl=600,
chunk=True,
chunk_overlap=0,
chunk_size=64,
diff --git a/tests/api_resources/aisearch/test_instances.py b/tests/api_resources/aisearch/test_instances.py
index 033ed5e4825..1aaf5fde4e6 100644
--- a/tests/api_resources/aisearch/test_instances.py
+++ b/tests/api_resources/aisearch/test_instances.py
@@ -44,6 +44,7 @@ def test_method_create_with_all_params(self, client: Cloudflare) -> None:
aisearch_model="@cf/meta/llama-3.3-70b-instruct-fp8-fast",
cache=True,
cache_threshold="super_strict_match",
+ cache_ttl=600,
chunk=True,
chunk_overlap=0,
chunk_size=64,
@@ -189,6 +190,7 @@ def test_method_update_with_all_params(self, client: Cloudflare) -> None:
aisearch_model="@cf/meta/llama-3.3-70b-instruct-fp8-fast",
cache=True,
cache_threshold="super_strict_match",
+ cache_ttl=600,
chunk=True,
chunk_overlap=0,
chunk_size=64,
@@ -765,6 +767,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncCloudflare
aisearch_model="@cf/meta/llama-3.3-70b-instruct-fp8-fast",
cache=True,
cache_threshold="super_strict_match",
+ cache_ttl=600,
chunk=True,
chunk_overlap=0,
chunk_size=64,
@@ -910,6 +913,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncCloudflare
aisearch_model="@cf/meta/llama-3.3-70b-instruct-fp8-fast",
cache=True,
cache_threshold="super_strict_match",
+ cache_ttl=600,
chunk=True,
chunk_overlap=0,
chunk_size=64,
diff --git a/tests/api_resources/email_security/test_submissions.py b/tests/api_resources/email_security/test_submissions.py
index a3ccfcd375d..d5600c91546 100644
--- a/tests/api_resources/email_security/test_submissions.py
+++ b/tests/api_resources/email_security/test_submissions.py
@@ -31,6 +31,7 @@ def test_method_list_with_all_params(self, client: Cloudflare) -> None:
submission = client.email_security.submissions.list(
account_id="023e105f4ecef8ad9ca31a8372d0c353",
end=parse_datetime("2019-12-27T18:11:19.117Z"),
+ escalated_from_user=True,
original_disposition="MALICIOUS",
outcome_disposition="MALICIOUS",
page=1,
@@ -93,6 +94,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncCloudflare)
submission = await async_client.email_security.submissions.list(
account_id="023e105f4ecef8ad9ca31a8372d0c353",
end=parse_datetime("2019-12-27T18:11:19.117Z"),
+ escalated_from_user=True,
original_disposition="MALICIOUS",
outcome_disposition="MALICIOUS",
page=1,
diff --git a/tests/api_resources/security_center/insights/test_audit_logs.py b/tests/api_resources/security_center/insights/test_audit_logs.py
new file mode 100644
index 00000000000..8316d66f419
--- /dev/null
+++ b/tests/api_resources/security_center/insights/test_audit_logs.py
@@ -0,0 +1,285 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from cloudflare import Cloudflare, AsyncCloudflare
+from tests.utils import assert_matches_type
+from cloudflare._utils import parse_datetime
+from cloudflare.pagination import SyncCursorPagination, AsyncCursorPagination
+from cloudflare.types.security_center.insights import (
+ AuditLogListResponse,
+ AuditLogListByInsightResponse,
+)
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestAuditLogs:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_list(self, client: Cloudflare) -> None:
+ audit_log = client.security_center.insights.audit_logs.list(
+ account_id="account_id",
+ )
+ assert_matches_type(SyncCursorPagination[AuditLogListResponse], audit_log, path=["response"])
+
+ @parametrize
+ def test_method_list_with_all_params(self, client: Cloudflare) -> None:
+ audit_log = client.security_center.insights.audit_logs.list(
+ account_id="account_id",
+ before=parse_datetime("2019-12-27T18:11:19.117Z"),
+ changed_by="changed_by",
+ cursor="cursor",
+ field_changed="status",
+ order="asc",
+ per_page=1,
+ since=parse_datetime("2019-12-27T18:11:19.117Z"),
+ )
+ assert_matches_type(SyncCursorPagination[AuditLogListResponse], audit_log, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: Cloudflare) -> None:
+ response = client.security_center.insights.audit_logs.with_raw_response.list(
+ account_id="account_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ audit_log = response.parse()
+ assert_matches_type(SyncCursorPagination[AuditLogListResponse], audit_log, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: Cloudflare) -> None:
+ with client.security_center.insights.audit_logs.with_streaming_response.list(
+ account_id="account_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ audit_log = response.parse()
+ assert_matches_type(SyncCursorPagination[AuditLogListResponse], audit_log, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="path_params test expects ValueError for account_id/zone_id but endpoint accepts both")
+ @parametrize
+ def test_path_params_list(self, client: Cloudflare) -> None:
+ with pytest.raises(ValueError, match=r"You must provide either account_id or zone_id"):
+ client.security_center.insights.audit_logs.with_raw_response.list(
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"You must provide either account_id or zone_id"):
+ client.security_center.insights.audit_logs.with_raw_response.list(
+ account_id="account_id",
+ )
+
+ @parametrize
+ def test_method_list_by_insight(self, client: Cloudflare) -> None:
+ audit_log = client.security_center.insights.audit_logs.list_by_insight(
+ issue_id="issue_id",
+ account_id="account_id",
+ )
+ assert_matches_type(SyncCursorPagination[AuditLogListByInsightResponse], audit_log, path=["response"])
+
+ @parametrize
+ def test_method_list_by_insight_with_all_params(self, client: Cloudflare) -> None:
+ audit_log = client.security_center.insights.audit_logs.list_by_insight(
+ issue_id="issue_id",
+ account_id="account_id",
+ before=parse_datetime("2019-12-27T18:11:19.117Z"),
+ changed_by="changed_by",
+ cursor="cursor",
+ field_changed="status",
+ order="asc",
+ per_page=1,
+ since=parse_datetime("2019-12-27T18:11:19.117Z"),
+ )
+ assert_matches_type(SyncCursorPagination[AuditLogListByInsightResponse], audit_log, path=["response"])
+
+ @parametrize
+ def test_raw_response_list_by_insight(self, client: Cloudflare) -> None:
+ response = client.security_center.insights.audit_logs.with_raw_response.list_by_insight(
+ issue_id="issue_id",
+ account_id="account_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ audit_log = response.parse()
+ assert_matches_type(SyncCursorPagination[AuditLogListByInsightResponse], audit_log, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list_by_insight(self, client: Cloudflare) -> None:
+ with client.security_center.insights.audit_logs.with_streaming_response.list_by_insight(
+ issue_id="issue_id",
+ account_id="account_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ audit_log = response.parse()
+ assert_matches_type(SyncCursorPagination[AuditLogListByInsightResponse], audit_log, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="path_params test expects ValueError for account_id/zone_id but endpoint accepts both")
+ @parametrize
+ def test_path_params_list_by_insight(self, client: Cloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `issue_id` but received ''"):
+ client.security_center.insights.audit_logs.with_raw_response.list_by_insight(
+ issue_id="",
+ account_id="account_id",
+ )
+
+ with pytest.raises(ValueError, match=r"You must provide either account_id or zone_id"):
+ client.security_center.insights.audit_logs.with_raw_response.list_by_insight(
+ issue_id="issue_id",
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"You must provide either account_id or zone_id"):
+ client.security_center.insights.audit_logs.with_raw_response.list_by_insight(
+ issue_id="issue_id",
+ account_id="account_id",
+ )
+
+
+class TestAsyncAuditLogs:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncCloudflare) -> None:
+ audit_log = await async_client.security_center.insights.audit_logs.list(
+ account_id="account_id",
+ )
+ assert_matches_type(AsyncCursorPagination[AuditLogListResponse], audit_log, path=["response"])
+
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncCloudflare) -> None:
+ audit_log = await async_client.security_center.insights.audit_logs.list(
+ account_id="account_id",
+ before=parse_datetime("2019-12-27T18:11:19.117Z"),
+ changed_by="changed_by",
+ cursor="cursor",
+ field_changed="status",
+ order="asc",
+ per_page=1,
+ since=parse_datetime("2019-12-27T18:11:19.117Z"),
+ )
+ assert_matches_type(AsyncCursorPagination[AuditLogListResponse], audit_log, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncCloudflare) -> None:
+ response = await async_client.security_center.insights.audit_logs.with_raw_response.list(
+ account_id="account_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ audit_log = await response.parse()
+ assert_matches_type(AsyncCursorPagination[AuditLogListResponse], audit_log, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncCloudflare) -> None:
+ async with async_client.security_center.insights.audit_logs.with_streaming_response.list(
+ account_id="account_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ audit_log = await response.parse()
+ assert_matches_type(AsyncCursorPagination[AuditLogListResponse], audit_log, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="path_params test expects ValueError for account_id/zone_id but endpoint accepts both")
+ @parametrize
+ async def test_path_params_list(self, async_client: AsyncCloudflare) -> None:
+ with pytest.raises(ValueError, match=r"You must provide either account_id or zone_id"):
+ await async_client.security_center.insights.audit_logs.with_raw_response.list(
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"You must provide either account_id or zone_id"):
+ await async_client.security_center.insights.audit_logs.with_raw_response.list(
+ account_id="account_id",
+ )
+
+ @parametrize
+ async def test_method_list_by_insight(self, async_client: AsyncCloudflare) -> None:
+ audit_log = await async_client.security_center.insights.audit_logs.list_by_insight(
+ issue_id="issue_id",
+ account_id="account_id",
+ )
+ assert_matches_type(AsyncCursorPagination[AuditLogListByInsightResponse], audit_log, path=["response"])
+
+ @parametrize
+ async def test_method_list_by_insight_with_all_params(self, async_client: AsyncCloudflare) -> None:
+ audit_log = await async_client.security_center.insights.audit_logs.list_by_insight(
+ issue_id="issue_id",
+ account_id="account_id",
+ before=parse_datetime("2019-12-27T18:11:19.117Z"),
+ changed_by="changed_by",
+ cursor="cursor",
+ field_changed="status",
+ order="asc",
+ per_page=1,
+ since=parse_datetime("2019-12-27T18:11:19.117Z"),
+ )
+ assert_matches_type(AsyncCursorPagination[AuditLogListByInsightResponse], audit_log, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list_by_insight(self, async_client: AsyncCloudflare) -> None:
+ response = await async_client.security_center.insights.audit_logs.with_raw_response.list_by_insight(
+ issue_id="issue_id",
+ account_id="account_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ audit_log = await response.parse()
+ assert_matches_type(AsyncCursorPagination[AuditLogListByInsightResponse], audit_log, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list_by_insight(self, async_client: AsyncCloudflare) -> None:
+ async with async_client.security_center.insights.audit_logs.with_streaming_response.list_by_insight(
+ issue_id="issue_id",
+ account_id="account_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ audit_log = await response.parse()
+ assert_matches_type(AsyncCursorPagination[AuditLogListByInsightResponse], audit_log, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="path_params test expects ValueError for account_id/zone_id but endpoint accepts both")
+ @parametrize
+ async def test_path_params_list_by_insight(self, async_client: AsyncCloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `issue_id` but received ''"):
+ await async_client.security_center.insights.audit_logs.with_raw_response.list_by_insight(
+ issue_id="",
+ account_id="account_id",
+ )
+
+ with pytest.raises(ValueError, match=r"You must provide either account_id or zone_id"):
+ await async_client.security_center.insights.audit_logs.with_raw_response.list_by_insight(
+ issue_id="issue_id",
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"You must provide either account_id or zone_id"):
+ await async_client.security_center.insights.audit_logs.with_raw_response.list_by_insight(
+ issue_id="issue_id",
+ account_id="account_id",
+ )
diff --git a/tests/api_resources/security_center/insights/test_classification.py b/tests/api_resources/security_center/insights/test_classification.py
new file mode 100644
index 00000000000..c5851341638
--- /dev/null
+++ b/tests/api_resources/security_center/insights/test_classification.py
@@ -0,0 +1,154 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from cloudflare import Cloudflare, AsyncCloudflare
+from tests.utils import assert_matches_type
+from cloudflare.types.security_center.insights import ClassificationUpdateResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestClassification:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_update(self, client: Cloudflare) -> None:
+ classification = client.security_center.insights.classification.update(
+ issue_id="issue_id",
+ account_id="account_id",
+ )
+ assert_matches_type(ClassificationUpdateResponse, classification, path=["response"])
+
+ @parametrize
+ def test_method_update_with_all_params(self, client: Cloudflare) -> None:
+ classification = client.security_center.insights.classification.update(
+ issue_id="issue_id",
+ account_id="account_id",
+ classification="false_positive",
+ rationale="rationale",
+ )
+ assert_matches_type(ClassificationUpdateResponse, classification, path=["response"])
+
+ @parametrize
+ def test_raw_response_update(self, client: Cloudflare) -> None:
+ response = client.security_center.insights.classification.with_raw_response.update(
+ issue_id="issue_id",
+ account_id="account_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ classification = response.parse()
+ assert_matches_type(ClassificationUpdateResponse, classification, path=["response"])
+
+ @parametrize
+ def test_streaming_response_update(self, client: Cloudflare) -> None:
+ with client.security_center.insights.classification.with_streaming_response.update(
+ issue_id="issue_id",
+ account_id="account_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ classification = response.parse()
+ assert_matches_type(ClassificationUpdateResponse, classification, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="path_params test expects ValueError for account_id/zone_id but endpoint accepts both")
+ @parametrize
+ def test_path_params_update(self, client: Cloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `issue_id` but received ''"):
+ client.security_center.insights.classification.with_raw_response.update(
+ issue_id="",
+ account_id="account_id",
+ )
+
+ with pytest.raises(ValueError, match=r"You must provide either account_id or zone_id"):
+ client.security_center.insights.classification.with_raw_response.update(
+ issue_id="issue_id",
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"You must provide either account_id or zone_id"):
+ client.security_center.insights.classification.with_raw_response.update(
+ issue_id="issue_id",
+ account_id="account_id",
+ )
+
+
+class TestAsyncClassification:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_update(self, async_client: AsyncCloudflare) -> None:
+ classification = await async_client.security_center.insights.classification.update(
+ issue_id="issue_id",
+ account_id="account_id",
+ )
+ assert_matches_type(ClassificationUpdateResponse, classification, path=["response"])
+
+ @parametrize
+ async def test_method_update_with_all_params(self, async_client: AsyncCloudflare) -> None:
+ classification = await async_client.security_center.insights.classification.update(
+ issue_id="issue_id",
+ account_id="account_id",
+ classification="false_positive",
+ rationale="rationale",
+ )
+ assert_matches_type(ClassificationUpdateResponse, classification, path=["response"])
+
+ @parametrize
+ async def test_raw_response_update(self, async_client: AsyncCloudflare) -> None:
+ response = await async_client.security_center.insights.classification.with_raw_response.update(
+ issue_id="issue_id",
+ account_id="account_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ classification = await response.parse()
+ assert_matches_type(ClassificationUpdateResponse, classification, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_update(self, async_client: AsyncCloudflare) -> None:
+ async with async_client.security_center.insights.classification.with_streaming_response.update(
+ issue_id="issue_id",
+ account_id="account_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ classification = await response.parse()
+ assert_matches_type(ClassificationUpdateResponse, classification, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="path_params test expects ValueError for account_id/zone_id but endpoint accepts both")
+ @parametrize
+ async def test_path_params_update(self, async_client: AsyncCloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `issue_id` but received ''"):
+ await async_client.security_center.insights.classification.with_raw_response.update(
+ issue_id="",
+ account_id="account_id",
+ )
+
+ with pytest.raises(ValueError, match=r"You must provide either account_id or zone_id"):
+ await async_client.security_center.insights.classification.with_raw_response.update(
+ issue_id="issue_id",
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"You must provide either account_id or zone_id"):
+ await async_client.security_center.insights.classification.with_raw_response.update(
+ issue_id="issue_id",
+ account_id="account_id",
+ )
diff --git a/tests/api_resources/security_center/insights/test_context.py b/tests/api_resources/security_center/insights/test_context.py
new file mode 100644
index 00000000000..0888ca0b618
--- /dev/null
+++ b/tests/api_resources/security_center/insights/test_context.py
@@ -0,0 +1,120 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, Optional, cast
+
+import pytest
+
+from cloudflare import Cloudflare, AsyncCloudflare
+from tests.utils import assert_matches_type
+from cloudflare.types.security_center.insights import ContextGetResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestContext:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_get(self, client: Cloudflare) -> None:
+ context = client.security_center.insights.context.get(
+ issue_id="issue_id",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+ assert_matches_type(Optional[ContextGetResponse], context, path=["response"])
+
+ @parametrize
+ def test_raw_response_get(self, client: Cloudflare) -> None:
+ response = client.security_center.insights.context.with_raw_response.get(
+ issue_id="issue_id",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ context = response.parse()
+ assert_matches_type(Optional[ContextGetResponse], context, path=["response"])
+
+ @parametrize
+ def test_streaming_response_get(self, client: Cloudflare) -> None:
+ with client.security_center.insights.context.with_streaming_response.get(
+ issue_id="issue_id",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ context = response.parse()
+ assert_matches_type(Optional[ContextGetResponse], context, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_get(self, client: Cloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ client.security_center.insights.context.with_raw_response.get(
+ issue_id="issue_id",
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `issue_id` but received ''"):
+ client.security_center.insights.context.with_raw_response.get(
+ issue_id="",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+
+
+class TestAsyncContext:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_get(self, async_client: AsyncCloudflare) -> None:
+ context = await async_client.security_center.insights.context.get(
+ issue_id="issue_id",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+ assert_matches_type(Optional[ContextGetResponse], context, path=["response"])
+
+ @parametrize
+ async def test_raw_response_get(self, async_client: AsyncCloudflare) -> None:
+ response = await async_client.security_center.insights.context.with_raw_response.get(
+ issue_id="issue_id",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ context = await response.parse()
+ assert_matches_type(Optional[ContextGetResponse], context, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_get(self, async_client: AsyncCloudflare) -> None:
+ async with async_client.security_center.insights.context.with_streaming_response.get(
+ issue_id="issue_id",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ context = await response.parse()
+ assert_matches_type(Optional[ContextGetResponse], context, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_get(self, async_client: AsyncCloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ await async_client.security_center.insights.context.with_raw_response.get(
+ issue_id="issue_id",
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `issue_id` but received ''"):
+ await async_client.security_center.insights.context.with_raw_response.get(
+ issue_id="",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
diff --git a/tests/api_resources/zero_trust/access/logs/scim/test_updates.py b/tests/api_resources/zero_trust/access/logs/scim/test_updates.py
index 8c4f2fd02e5..28f3ed97e00 100644
--- a/tests/api_resources/zero_trust/access/logs/scim/test_updates.py
+++ b/tests/api_resources/zero_trust/access/logs/scim/test_updates.py
@@ -32,16 +32,16 @@ def test_method_list_with_all_params(self, client: Cloudflare) -> None:
update = client.zero_trust.access.logs.scim.updates.list(
account_id="023e105f4ecef8ad9ca31a8372d0c353",
idp_id=["df7e2w5f-02b7-4d9d-af26-8d1988fca630", "0194ae2c-efcf-7cfb-8884-055f1a161fa5"],
- cf_resource_id="bd97ef8d-7986-43e3-9ee0-c25dda33e4b0",
+ cf_resource_id=["bd97ef8d-7986-43e3-9ee0-c25dda33e4b0"],
direction="desc",
- idp_resource_id="idp_resource_id",
+ idp_resource_id=["all_employees"],
limit=10,
page=0,
per_page=0,
request_method=["DELETE", "PATCH"],
- resource_group_name="ALL_EMPLOYEES",
+ resource_group_name=["ALL_EMPLOYEES"],
resource_type=["USER", "GROUP"],
- resource_user_email="john.smith@example.com",
+ resource_user_email=["john.smith@example.com"],
since=parse_datetime("2025-01-01T00:00:00Z"),
status=["FAILURE", "SUCCESS"],
until=parse_datetime("2025-01-02T00:00:00Z"),
@@ -101,16 +101,16 @@ async def test_method_list_with_all_params(self, async_client: AsyncCloudflare)
update = await async_client.zero_trust.access.logs.scim.updates.list(
account_id="023e105f4ecef8ad9ca31a8372d0c353",
idp_id=["df7e2w5f-02b7-4d9d-af26-8d1988fca630", "0194ae2c-efcf-7cfb-8884-055f1a161fa5"],
- cf_resource_id="bd97ef8d-7986-43e3-9ee0-c25dda33e4b0",
+ cf_resource_id=["bd97ef8d-7986-43e3-9ee0-c25dda33e4b0"],
direction="desc",
- idp_resource_id="idp_resource_id",
+ idp_resource_id=["all_employees"],
limit=10,
page=0,
per_page=0,
request_method=["DELETE", "PATCH"],
- resource_group_name="ALL_EMPLOYEES",
+ resource_group_name=["ALL_EMPLOYEES"],
resource_type=["USER", "GROUP"],
- resource_user_email="john.smith@example.com",
+ resource_user_email=["john.smith@example.com"],
since=parse_datetime("2025-01-01T00:00:00Z"),
status=["FAILURE", "SUCCESS"],
until=parse_datetime("2025-01-02T00:00:00Z"),
diff --git a/tests/api_resources/zero_trust/devices/test_deployment_groups.py b/tests/api_resources/zero_trust/devices/test_deployment_groups.py
new file mode 100644
index 00000000000..98dd6882828
--- /dev/null
+++ b/tests/api_resources/zero_trust/devices/test_deployment_groups.py
@@ -0,0 +1,650 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from cloudflare import Cloudflare, AsyncCloudflare
+from tests.utils import assert_matches_type
+from cloudflare.pagination import SyncV4PagePaginationArray, AsyncV4PagePaginationArray
+from cloudflare.types.zero_trust.devices import (
+ DeploymentGroup,
+ DeploymentGroupDeleteResponse,
+)
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestDeploymentGroups:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ def test_method_create(self, client: Cloudflare) -> None:
+ deployment_group = client.zero_trust.devices.deployment_groups.create(
+ account_id="account_id",
+ name="Engineering Ring 0",
+ version_config=[
+ {
+ "target_environment": "windows",
+ "version": "2026.5.234.0",
+ }
+ ],
+ )
+ assert_matches_type(DeploymentGroup, deployment_group, path=["response"])
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ def test_method_create_with_all_params(self, client: Cloudflare) -> None:
+ deployment_group = client.zero_trust.devices.deployment_groups.create(
+ account_id="account_id",
+ name="Engineering Ring 0",
+ version_config=[
+ {
+ "target_environment": "windows",
+ "version": "2026.5.234.0",
+ }
+ ],
+ policy_ids=["string"],
+ )
+ assert_matches_type(DeploymentGroup, deployment_group, path=["response"])
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ def test_raw_response_create(self, client: Cloudflare) -> None:
+ response = client.zero_trust.devices.deployment_groups.with_raw_response.create(
+ account_id="account_id",
+ name="Engineering Ring 0",
+ version_config=[
+ {
+ "target_environment": "windows",
+ "version": "2026.5.234.0",
+ }
+ ],
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ deployment_group = response.parse()
+ assert_matches_type(DeploymentGroup, deployment_group, path=["response"])
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ def test_streaming_response_create(self, client: Cloudflare) -> None:
+ with client.zero_trust.devices.deployment_groups.with_streaming_response.create(
+ account_id="account_id",
+ name="Engineering Ring 0",
+ version_config=[
+ {
+ "target_environment": "windows",
+ "version": "2026.5.234.0",
+ }
+ ],
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ deployment_group = response.parse()
+ assert_matches_type(DeploymentGroup, deployment_group, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ def test_path_params_create(self, client: Cloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ client.zero_trust.devices.deployment_groups.with_raw_response.create(
+ account_id="",
+ name="Engineering Ring 0",
+ version_config=[
+ {
+ "target_environment": "windows",
+ "version": "2026.5.234.0",
+ }
+ ],
+ )
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ def test_method_list(self, client: Cloudflare) -> None:
+ deployment_group = client.zero_trust.devices.deployment_groups.list(
+ account_id="account_id",
+ )
+ assert_matches_type(SyncV4PagePaginationArray[DeploymentGroup], deployment_group, path=["response"])
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ def test_method_list_with_all_params(self, client: Cloudflare) -> None:
+ deployment_group = client.zero_trust.devices.deployment_groups.list(
+ account_id="account_id",
+ page=1,
+ per_page=1,
+ )
+ assert_matches_type(SyncV4PagePaginationArray[DeploymentGroup], deployment_group, path=["response"])
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ def test_raw_response_list(self, client: Cloudflare) -> None:
+ response = client.zero_trust.devices.deployment_groups.with_raw_response.list(
+ account_id="account_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ deployment_group = response.parse()
+ assert_matches_type(SyncV4PagePaginationArray[DeploymentGroup], deployment_group, path=["response"])
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ def test_streaming_response_list(self, client: Cloudflare) -> None:
+ with client.zero_trust.devices.deployment_groups.with_streaming_response.list(
+ account_id="account_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ deployment_group = response.parse()
+ assert_matches_type(SyncV4PagePaginationArray[DeploymentGroup], deployment_group, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ def test_path_params_list(self, client: Cloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ client.zero_trust.devices.deployment_groups.with_raw_response.list(
+ account_id="",
+ )
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ def test_method_delete(self, client: Cloudflare) -> None:
+ deployment_group = client.zero_trust.devices.deployment_groups.delete(
+ group_id="group_id",
+ account_id="account_id",
+ )
+ assert_matches_type(DeploymentGroupDeleteResponse, deployment_group, path=["response"])
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ def test_raw_response_delete(self, client: Cloudflare) -> None:
+ response = client.zero_trust.devices.deployment_groups.with_raw_response.delete(
+ group_id="group_id",
+ account_id="account_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ deployment_group = response.parse()
+ assert_matches_type(DeploymentGroupDeleteResponse, deployment_group, path=["response"])
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ def test_streaming_response_delete(self, client: Cloudflare) -> None:
+ with client.zero_trust.devices.deployment_groups.with_streaming_response.delete(
+ group_id="group_id",
+ account_id="account_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ deployment_group = response.parse()
+ assert_matches_type(DeploymentGroupDeleteResponse, deployment_group, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ def test_path_params_delete(self, client: Cloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ client.zero_trust.devices.deployment_groups.with_raw_response.delete(
+ group_id="group_id",
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"):
+ client.zero_trust.devices.deployment_groups.with_raw_response.delete(
+ group_id="",
+ account_id="account_id",
+ )
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ def test_method_edit(self, client: Cloudflare) -> None:
+ deployment_group = client.zero_trust.devices.deployment_groups.edit(
+ group_id="group_id",
+ account_id="account_id",
+ )
+ assert_matches_type(DeploymentGroup, deployment_group, path=["response"])
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ def test_method_edit_with_all_params(self, client: Cloudflare) -> None:
+ deployment_group = client.zero_trust.devices.deployment_groups.edit(
+ group_id="group_id",
+ account_id="account_id",
+ name="Engineering Ring 0",
+ policy_ids=["string"],
+ version_config=[
+ {
+ "target_environment": "windows",
+ "version": "2026.5.234.0",
+ }
+ ],
+ )
+ assert_matches_type(DeploymentGroup, deployment_group, path=["response"])
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ def test_raw_response_edit(self, client: Cloudflare) -> None:
+ response = client.zero_trust.devices.deployment_groups.with_raw_response.edit(
+ group_id="group_id",
+ account_id="account_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ deployment_group = response.parse()
+ assert_matches_type(DeploymentGroup, deployment_group, path=["response"])
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ def test_streaming_response_edit(self, client: Cloudflare) -> None:
+ with client.zero_trust.devices.deployment_groups.with_streaming_response.edit(
+ group_id="group_id",
+ account_id="account_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ deployment_group = response.parse()
+ assert_matches_type(DeploymentGroup, deployment_group, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ def test_path_params_edit(self, client: Cloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ client.zero_trust.devices.deployment_groups.with_raw_response.edit(
+ group_id="group_id",
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"):
+ client.zero_trust.devices.deployment_groups.with_raw_response.edit(
+ group_id="",
+ account_id="account_id",
+ )
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ def test_method_get(self, client: Cloudflare) -> None:
+ deployment_group = client.zero_trust.devices.deployment_groups.get(
+ group_id="group_id",
+ account_id="account_id",
+ )
+ assert_matches_type(DeploymentGroup, deployment_group, path=["response"])
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ def test_raw_response_get(self, client: Cloudflare) -> None:
+ response = client.zero_trust.devices.deployment_groups.with_raw_response.get(
+ group_id="group_id",
+ account_id="account_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ deployment_group = response.parse()
+ assert_matches_type(DeploymentGroup, deployment_group, path=["response"])
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ def test_streaming_response_get(self, client: Cloudflare) -> None:
+ with client.zero_trust.devices.deployment_groups.with_streaming_response.get(
+ group_id="group_id",
+ account_id="account_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ deployment_group = response.parse()
+ assert_matches_type(DeploymentGroup, deployment_group, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ def test_path_params_get(self, client: Cloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ client.zero_trust.devices.deployment_groups.with_raw_response.get(
+ group_id="group_id",
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"):
+ client.zero_trust.devices.deployment_groups.with_raw_response.get(
+ group_id="",
+ account_id="account_id",
+ )
+
+
+class TestAsyncDeploymentGroups:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ async def test_method_create(self, async_client: AsyncCloudflare) -> None:
+ deployment_group = await async_client.zero_trust.devices.deployment_groups.create(
+ account_id="account_id",
+ name="Engineering Ring 0",
+ version_config=[
+ {
+ "target_environment": "windows",
+ "version": "2026.5.234.0",
+ }
+ ],
+ )
+ assert_matches_type(DeploymentGroup, deployment_group, path=["response"])
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncCloudflare) -> None:
+ deployment_group = await async_client.zero_trust.devices.deployment_groups.create(
+ account_id="account_id",
+ name="Engineering Ring 0",
+ version_config=[
+ {
+ "target_environment": "windows",
+ "version": "2026.5.234.0",
+ }
+ ],
+ policy_ids=["string"],
+ )
+ assert_matches_type(DeploymentGroup, deployment_group, path=["response"])
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncCloudflare) -> None:
+ response = await async_client.zero_trust.devices.deployment_groups.with_raw_response.create(
+ account_id="account_id",
+ name="Engineering Ring 0",
+ version_config=[
+ {
+ "target_environment": "windows",
+ "version": "2026.5.234.0",
+ }
+ ],
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ deployment_group = await response.parse()
+ assert_matches_type(DeploymentGroup, deployment_group, path=["response"])
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncCloudflare) -> None:
+ async with async_client.zero_trust.devices.deployment_groups.with_streaming_response.create(
+ account_id="account_id",
+ name="Engineering Ring 0",
+ version_config=[
+ {
+ "target_environment": "windows",
+ "version": "2026.5.234.0",
+ }
+ ],
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ deployment_group = await response.parse()
+ assert_matches_type(DeploymentGroup, deployment_group, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ async def test_path_params_create(self, async_client: AsyncCloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ await async_client.zero_trust.devices.deployment_groups.with_raw_response.create(
+ account_id="",
+ name="Engineering Ring 0",
+ version_config=[
+ {
+ "target_environment": "windows",
+ "version": "2026.5.234.0",
+ }
+ ],
+ )
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ async def test_method_list(self, async_client: AsyncCloudflare) -> None:
+ deployment_group = await async_client.zero_trust.devices.deployment_groups.list(
+ account_id="account_id",
+ )
+ assert_matches_type(AsyncV4PagePaginationArray[DeploymentGroup], deployment_group, path=["response"])
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncCloudflare) -> None:
+ deployment_group = await async_client.zero_trust.devices.deployment_groups.list(
+ account_id="account_id",
+ page=1,
+ per_page=1,
+ )
+ assert_matches_type(AsyncV4PagePaginationArray[DeploymentGroup], deployment_group, path=["response"])
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncCloudflare) -> None:
+ response = await async_client.zero_trust.devices.deployment_groups.with_raw_response.list(
+ account_id="account_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ deployment_group = await response.parse()
+ assert_matches_type(AsyncV4PagePaginationArray[DeploymentGroup], deployment_group, path=["response"])
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncCloudflare) -> None:
+ async with async_client.zero_trust.devices.deployment_groups.with_streaming_response.list(
+ account_id="account_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ deployment_group = await response.parse()
+ assert_matches_type(AsyncV4PagePaginationArray[DeploymentGroup], deployment_group, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ async def test_path_params_list(self, async_client: AsyncCloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ await async_client.zero_trust.devices.deployment_groups.with_raw_response.list(
+ account_id="",
+ )
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncCloudflare) -> None:
+ deployment_group = await async_client.zero_trust.devices.deployment_groups.delete(
+ group_id="group_id",
+ account_id="account_id",
+ )
+ assert_matches_type(DeploymentGroupDeleteResponse, deployment_group, path=["response"])
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncCloudflare) -> None:
+ response = await async_client.zero_trust.devices.deployment_groups.with_raw_response.delete(
+ group_id="group_id",
+ account_id="account_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ deployment_group = await response.parse()
+ assert_matches_type(DeploymentGroupDeleteResponse, deployment_group, path=["response"])
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncCloudflare) -> None:
+ async with async_client.zero_trust.devices.deployment_groups.with_streaming_response.delete(
+ group_id="group_id",
+ account_id="account_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ deployment_group = await response.parse()
+ assert_matches_type(DeploymentGroupDeleteResponse, deployment_group, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ async def test_path_params_delete(self, async_client: AsyncCloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ await async_client.zero_trust.devices.deployment_groups.with_raw_response.delete(
+ group_id="group_id",
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"):
+ await async_client.zero_trust.devices.deployment_groups.with_raw_response.delete(
+ group_id="",
+ account_id="account_id",
+ )
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ async def test_method_edit(self, async_client: AsyncCloudflare) -> None:
+ deployment_group = await async_client.zero_trust.devices.deployment_groups.edit(
+ group_id="group_id",
+ account_id="account_id",
+ )
+ assert_matches_type(DeploymentGroup, deployment_group, path=["response"])
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ async def test_method_edit_with_all_params(self, async_client: AsyncCloudflare) -> None:
+ deployment_group = await async_client.zero_trust.devices.deployment_groups.edit(
+ group_id="group_id",
+ account_id="account_id",
+ name="Engineering Ring 0",
+ policy_ids=["string"],
+ version_config=[
+ {
+ "target_environment": "windows",
+ "version": "2026.5.234.0",
+ }
+ ],
+ )
+ assert_matches_type(DeploymentGroup, deployment_group, path=["response"])
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ async def test_raw_response_edit(self, async_client: AsyncCloudflare) -> None:
+ response = await async_client.zero_trust.devices.deployment_groups.with_raw_response.edit(
+ group_id="group_id",
+ account_id="account_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ deployment_group = await response.parse()
+ assert_matches_type(DeploymentGroup, deployment_group, path=["response"])
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ async def test_streaming_response_edit(self, async_client: AsyncCloudflare) -> None:
+ async with async_client.zero_trust.devices.deployment_groups.with_streaming_response.edit(
+ group_id="group_id",
+ account_id="account_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ deployment_group = await response.parse()
+ assert_matches_type(DeploymentGroup, deployment_group, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ async def test_path_params_edit(self, async_client: AsyncCloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ await async_client.zero_trust.devices.deployment_groups.with_raw_response.edit(
+ group_id="group_id",
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"):
+ await async_client.zero_trust.devices.deployment_groups.with_raw_response.edit(
+ group_id="",
+ account_id="account_id",
+ )
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ async def test_method_get(self, async_client: AsyncCloudflare) -> None:
+ deployment_group = await async_client.zero_trust.devices.deployment_groups.get(
+ group_id="group_id",
+ account_id="account_id",
+ )
+ assert_matches_type(DeploymentGroup, deployment_group, path=["response"])
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ async def test_raw_response_get(self, async_client: AsyncCloudflare) -> None:
+ response = await async_client.zero_trust.devices.deployment_groups.with_raw_response.get(
+ group_id="group_id",
+ account_id="account_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ deployment_group = await response.parse()
+ assert_matches_type(DeploymentGroup, deployment_group, path=["response"])
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ async def test_streaming_response_get(self, async_client: AsyncCloudflare) -> None:
+ async with async_client.zero_trust.devices.deployment_groups.with_streaming_response.get(
+ group_id="group_id",
+ account_id="account_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ deployment_group = await response.parse()
+ assert_matches_type(DeploymentGroup, deployment_group, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @pytest.mark.skip(reason="HTTP 401 error from prism")
+ @parametrize
+ async def test_path_params_get(self, async_client: AsyncCloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ await async_client.zero_trust.devices.deployment_groups.with_raw_response.get(
+ group_id="group_id",
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"):
+ await async_client.zero_trust.devices.deployment_groups.with_raw_response.get(
+ group_id="",
+ account_id="account_id",
+ )
diff --git a/tests/api_resources/zero_trust/identity_providers/scim/test_groups.py b/tests/api_resources/zero_trust/identity_providers/scim/test_groups.py
index c19dce42b04..170f83a0043 100644
--- a/tests/api_resources/zero_trust/identity_providers/scim/test_groups.py
+++ b/tests/api_resources/zero_trust/identity_providers/scim/test_groups.py
@@ -31,8 +31,8 @@ def test_method_list_with_all_params(self, client: Cloudflare) -> None:
group = client.zero_trust.identity_providers.scim.groups.list(
identity_provider_id="f174e90a-fafe-4643-bbbc-4a0ed4fc8415",
account_id="023e105f4ecef8ad9ca31a8372d0c353",
- cf_resource_id="a2abeb50-59c9-4c01-8c5c-963d3bf5700f",
- idp_resource_id="all_employees",
+ cf_resource_id=["a2abeb50-59c9-4c01-8c5c-963d3bf5700f"],
+ idp_resource_id=["all_employees"],
name="ALL_EMPLOYEES",
page=0,
per_page=0,
@@ -98,8 +98,8 @@ async def test_method_list_with_all_params(self, async_client: AsyncCloudflare)
group = await async_client.zero_trust.identity_providers.scim.groups.list(
identity_provider_id="f174e90a-fafe-4643-bbbc-4a0ed4fc8415",
account_id="023e105f4ecef8ad9ca31a8372d0c353",
- cf_resource_id="a2abeb50-59c9-4c01-8c5c-963d3bf5700f",
- idp_resource_id="all_employees",
+ cf_resource_id=["a2abeb50-59c9-4c01-8c5c-963d3bf5700f"],
+ idp_resource_id=["all_employees"],
name="ALL_EMPLOYEES",
page=0,
per_page=0,
diff --git a/tests/api_resources/zero_trust/identity_providers/scim/test_users.py b/tests/api_resources/zero_trust/identity_providers/scim/test_users.py
index 8d1eebe0ccf..a3f57d24030 100644
--- a/tests/api_resources/zero_trust/identity_providers/scim/test_users.py
+++ b/tests/api_resources/zero_trust/identity_providers/scim/test_users.py
@@ -31,9 +31,9 @@ def test_method_list_with_all_params(self, client: Cloudflare) -> None:
user = client.zero_trust.identity_providers.scim.users.list(
identity_provider_id="f174e90a-fafe-4643-bbbc-4a0ed4fc8415",
account_id="023e105f4ecef8ad9ca31a8372d0c353",
- cf_resource_id="bd97ef8d-7986-43e3-9ee0-c25dda33e4b0",
+ cf_resource_id=["bd97ef8d-7986-43e3-9ee0-c25dda33e4b0"],
email="john.smith@example.com",
- idp_resource_id="john_smith_01",
+ idp_resource_id=["john_smith_01"],
name="John Smith",
page=0,
per_page=0,
@@ -100,9 +100,9 @@ async def test_method_list_with_all_params(self, async_client: AsyncCloudflare)
user = await async_client.zero_trust.identity_providers.scim.users.list(
identity_provider_id="f174e90a-fafe-4643-bbbc-4a0ed4fc8415",
account_id="023e105f4ecef8ad9ca31a8372d0c353",
- cf_resource_id="bd97ef8d-7986-43e3-9ee0-c25dda33e4b0",
+ cf_resource_id=["bd97ef8d-7986-43e3-9ee0-c25dda33e4b0"],
email="john.smith@example.com",
- idp_resource_id="john_smith_01",
+ idp_resource_id=["john_smith_01"],
name="John Smith",
page=0,
per_page=0,