diff --git a/.stats.yml b/.stats.yml
index ca71746c0ec..67bc894f95f 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 2404
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-874436f83bd9c383144c69da47c4b767bb9c6f4f2bb4945af58cf3b6015f0f62.yml
-openapi_spec_hash: beaf9a654991bf65d642e05c03460e4c
-config_hash: 2f529580a17438fc62cd0b47db41b6f1
+configured_endpoints: 2412
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare/cloudflare-ee348bae3bc19d58249c0a0334ea1be47869fd3c73ea0cbbf505f185acc85393.yml
+openapi_spec_hash: ce574d5447a6a01dde1d36709a279533
+config_hash: a23f4c71ccd6c6117c24f63ae415dcf7
diff --git a/api.md b/api.md
index b05e7b8d057..1abe5e33c0f 100644
--- a/api.md
+++ b/api.md
@@ -75,6 +75,8 @@ from cloudflare.types import (
# [EmailSecurity](src/cloudflare/resources/email_security/api.md)
+# [EmailAuth](src/cloudflare/resources/email_auth/api.md)
+
# [EmailRouting](src/cloudflare/resources/email_routing/api.md)
# [EmailSending](src/cloudflare/resources/email_sending/api.md)
@@ -211,6 +213,8 @@ from cloudflare.types import (
# [Calls](src/cloudflare/resources/calls/api.md)
+# [MoQ](src/cloudflare/resources/moq/api.md)
+
# [CloudforceOne](src/cloudflare/resources/cloudforce_one/api.md)
# [AIGateway](src/cloudflare/resources/ai_gateway/api.md)
diff --git a/src/cloudflare/_client.py b/src/cloudflare/_client.py
index af5c148ccc9..51991d7bc79 100644
--- a/src/cloudflare/_client.py
+++ b/src/cloudflare/_client.py
@@ -47,6 +47,7 @@
dns,
iam,
ips,
+ moq,
rum,
ssl,
argo,
@@ -88,6 +89,7 @@
addressing,
ai_gateway,
audit_logs,
+ email_auth,
hyperdrive,
page_rules,
zero_trust,
@@ -163,6 +165,7 @@
from .resources.dns.dns import DNSResource, AsyncDNSResource
from .resources.iam.iam import IAMResource, AsyncIAMResource
from .resources.ips.ips import IPsResource, AsyncIPsResource
+ from .resources.moq.moq import MoQResource, AsyncMoQResource
from .resources.rum.rum import RUMResource, AsyncRUMResource
from .resources.ssl.ssl import SSLResource, AsyncSSLResource
from .resources.argo.argo import ArgoResource, AsyncArgoResource
@@ -204,6 +207,7 @@
from .resources.addressing.addressing import AddressingResource, AsyncAddressingResource
from .resources.ai_gateway.ai_gateway import AIGatewayResource, AsyncAIGatewayResource
from .resources.audit_logs.audit_logs import AuditLogsResource, AsyncAuditLogsResource
+ from .resources.email_auth.email_auth import EmailAuthResource, AsyncEmailAuthResource
from .resources.hyperdrive.hyperdrive import HyperdriveResource, AsyncHyperdriveResource
from .resources.page_rules.page_rules import PageRulesResource, AsyncPageRulesResource
from .resources.zero_trust.zero_trust import ZeroTrustResource, AsyncZeroTrustResource
@@ -557,6 +561,12 @@ def email_security(self) -> EmailSecurityResource:
return EmailSecurityResource(self)
+ @cached_property
+ def email_auth(self) -> EmailAuthResource:
+ from .resources.email_auth import EmailAuthResource
+
+ return EmailAuthResource(self)
+
@cached_property
def email_routing(self) -> EmailRoutingResource:
from .resources.email_routing import EmailRoutingResource
@@ -965,6 +975,12 @@ def calls(self) -> CallsResource:
return CallsResource(self)
+ @cached_property
+ def moq(self) -> MoQResource:
+ from .resources.moq import MoQResource
+
+ return MoQResource(self)
+
@cached_property
def cloudforce_one(self) -> CloudforceOneResource:
from .resources.cloudforce_one import CloudforceOneResource
@@ -1509,6 +1525,12 @@ def email_security(self) -> AsyncEmailSecurityResource:
return AsyncEmailSecurityResource(self)
+ @cached_property
+ def email_auth(self) -> AsyncEmailAuthResource:
+ from .resources.email_auth import AsyncEmailAuthResource
+
+ return AsyncEmailAuthResource(self)
+
@cached_property
def email_routing(self) -> AsyncEmailRoutingResource:
from .resources.email_routing import AsyncEmailRoutingResource
@@ -1917,6 +1939,12 @@ def calls(self) -> AsyncCallsResource:
return AsyncCallsResource(self)
+ @cached_property
+ def moq(self) -> AsyncMoQResource:
+ from .resources.moq import AsyncMoQResource
+
+ return AsyncMoQResource(self)
+
@cached_property
def cloudforce_one(self) -> AsyncCloudforceOneResource:
from .resources.cloudforce_one import AsyncCloudforceOneResource
@@ -2381,6 +2409,12 @@ def email_security(self) -> email_security.EmailSecurityResourceWithRawResponse:
return EmailSecurityResourceWithRawResponse(self._client.email_security)
+ @cached_property
+ def email_auth(self) -> email_auth.EmailAuthResourceWithRawResponse:
+ from .resources.email_auth import EmailAuthResourceWithRawResponse
+
+ return EmailAuthResourceWithRawResponse(self._client.email_auth)
+
@cached_property
def email_routing(self) -> email_routing.EmailRoutingResourceWithRawResponse:
from .resources.email_routing import EmailRoutingResourceWithRawResponse
@@ -2793,6 +2827,12 @@ def calls(self) -> calls.CallsResourceWithRawResponse:
return CallsResourceWithRawResponse(self._client.calls)
+ @cached_property
+ def moq(self) -> moq.MoQResourceWithRawResponse:
+ from .resources.moq import MoQResourceWithRawResponse
+
+ return MoQResourceWithRawResponse(self._client.moq)
+
@cached_property
def cloudforce_one(self) -> cloudforce_one.CloudforceOneResourceWithRawResponse:
from .resources.cloudforce_one import CloudforceOneResourceWithRawResponse
@@ -3084,6 +3124,12 @@ def email_security(self) -> email_security.AsyncEmailSecurityResourceWithRawResp
return AsyncEmailSecurityResourceWithRawResponse(self._client.email_security)
+ @cached_property
+ def email_auth(self) -> email_auth.AsyncEmailAuthResourceWithRawResponse:
+ from .resources.email_auth import AsyncEmailAuthResourceWithRawResponse
+
+ return AsyncEmailAuthResourceWithRawResponse(self._client.email_auth)
+
@cached_property
def email_routing(self) -> email_routing.AsyncEmailRoutingResourceWithRawResponse:
from .resources.email_routing import AsyncEmailRoutingResourceWithRawResponse
@@ -3496,6 +3542,12 @@ def calls(self) -> calls.AsyncCallsResourceWithRawResponse:
return AsyncCallsResourceWithRawResponse(self._client.calls)
+ @cached_property
+ def moq(self) -> moq.AsyncMoQResourceWithRawResponse:
+ from .resources.moq import AsyncMoQResourceWithRawResponse
+
+ return AsyncMoQResourceWithRawResponse(self._client.moq)
+
@cached_property
def cloudforce_one(self) -> cloudforce_one.AsyncCloudforceOneResourceWithRawResponse:
from .resources.cloudforce_one import AsyncCloudforceOneResourceWithRawResponse
@@ -3787,6 +3839,12 @@ def email_security(self) -> email_security.EmailSecurityResourceWithStreamingRes
return EmailSecurityResourceWithStreamingResponse(self._client.email_security)
+ @cached_property
+ def email_auth(self) -> email_auth.EmailAuthResourceWithStreamingResponse:
+ from .resources.email_auth import EmailAuthResourceWithStreamingResponse
+
+ return EmailAuthResourceWithStreamingResponse(self._client.email_auth)
+
@cached_property
def email_routing(self) -> email_routing.EmailRoutingResourceWithStreamingResponse:
from .resources.email_routing import EmailRoutingResourceWithStreamingResponse
@@ -4199,6 +4257,12 @@ def calls(self) -> calls.CallsResourceWithStreamingResponse:
return CallsResourceWithStreamingResponse(self._client.calls)
+ @cached_property
+ def moq(self) -> moq.MoQResourceWithStreamingResponse:
+ from .resources.moq import MoQResourceWithStreamingResponse
+
+ return MoQResourceWithStreamingResponse(self._client.moq)
+
@cached_property
def cloudforce_one(self) -> cloudforce_one.CloudforceOneResourceWithStreamingResponse:
from .resources.cloudforce_one import CloudforceOneResourceWithStreamingResponse
@@ -4492,6 +4556,12 @@ def email_security(self) -> email_security.AsyncEmailSecurityResourceWithStreami
return AsyncEmailSecurityResourceWithStreamingResponse(self._client.email_security)
+ @cached_property
+ def email_auth(self) -> email_auth.AsyncEmailAuthResourceWithStreamingResponse:
+ from .resources.email_auth import AsyncEmailAuthResourceWithStreamingResponse
+
+ return AsyncEmailAuthResourceWithStreamingResponse(self._client.email_auth)
+
@cached_property
def email_routing(self) -> email_routing.AsyncEmailRoutingResourceWithStreamingResponse:
from .resources.email_routing import AsyncEmailRoutingResourceWithStreamingResponse
@@ -4910,6 +4980,12 @@ def calls(self) -> calls.AsyncCallsResourceWithStreamingResponse:
return AsyncCallsResourceWithStreamingResponse(self._client.calls)
+ @cached_property
+ def moq(self) -> moq.AsyncMoQResourceWithStreamingResponse:
+ from .resources.moq import AsyncMoQResourceWithStreamingResponse
+
+ return AsyncMoQResourceWithStreamingResponse(self._client.moq)
+
@cached_property
def cloudforce_one(self) -> cloudforce_one.AsyncCloudforceOneResourceWithStreamingResponse:
from .resources.cloudforce_one import AsyncCloudforceOneResourceWithStreamingResponse
diff --git a/src/cloudflare/resources/ai_gateway/billing/spending_limit.py b/src/cloudflare/resources/ai_gateway/billing/spending_limit.py
index 8235d6ccb1c..1d7a18a89a4 100644
--- a/src/cloudflare/resources/ai_gateway/billing/spending_limit.py
+++ b/src/cloudflare/resources/ai_gateway/billing/spending_limit.py
@@ -2,6 +2,7 @@
from __future__ import annotations
+import typing_extensions
from typing import Type, cast
from typing_extensions import Literal
@@ -45,6 +46,7 @@ def with_streaming_response(self) -> SpendingLimitResourceWithStreamingResponse:
"""
return SpendingLimitResourceWithStreamingResponse(self)
+ @typing_extensions.deprecated("deprecated")
def create(
self,
*,
@@ -60,7 +62,10 @@ def create(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> object:
"""
- Configure a spending limit with amount, strategy, and duration.
+ Deprecated: spending limits can no longer be created, enabled, or modified and
+ this endpoint always responds 403. Use the new AI Gateway spend limits instead:
+ https://developers.cloudflare.com/ai-gateway/features/spend-limits/. Existing
+ limits can be removed via DELETE /spending-limit.
Args:
amount: Spending limit amount in cents (min 100).
@@ -194,6 +199,7 @@ def with_streaming_response(self) -> AsyncSpendingLimitResourceWithStreamingResp
"""
return AsyncSpendingLimitResourceWithStreamingResponse(self)
+ @typing_extensions.deprecated("deprecated")
async def create(
self,
*,
@@ -209,7 +215,10 @@ async def create(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> object:
"""
- Configure a spending limit with amount, strategy, and duration.
+ Deprecated: spending limits can no longer be created, enabled, or modified and
+ this endpoint always responds 403. Use the new AI Gateway spend limits instead:
+ https://developers.cloudflare.com/ai-gateway/features/spend-limits/. Existing
+ limits can be removed via DELETE /spending-limit.
Args:
amount: Spending limit amount in cents (min 100).
@@ -327,8 +336,10 @@ class SpendingLimitResourceWithRawResponse:
def __init__(self, spending_limit: SpendingLimitResource) -> None:
self._spending_limit = spending_limit
- self.create = to_raw_response_wrapper(
- spending_limit.create,
+ self.create = ( # pyright: ignore[reportDeprecated]
+ to_raw_response_wrapper(
+ spending_limit.create, # pyright: ignore[reportDeprecated],
+ )
)
self.delete = to_raw_response_wrapper(
spending_limit.delete,
@@ -342,8 +353,10 @@ class AsyncSpendingLimitResourceWithRawResponse:
def __init__(self, spending_limit: AsyncSpendingLimitResource) -> None:
self._spending_limit = spending_limit
- self.create = async_to_raw_response_wrapper(
- spending_limit.create,
+ self.create = ( # pyright: ignore[reportDeprecated]
+ async_to_raw_response_wrapper(
+ spending_limit.create, # pyright: ignore[reportDeprecated],
+ )
)
self.delete = async_to_raw_response_wrapper(
spending_limit.delete,
@@ -357,8 +370,10 @@ class SpendingLimitResourceWithStreamingResponse:
def __init__(self, spending_limit: SpendingLimitResource) -> None:
self._spending_limit = spending_limit
- self.create = to_streamed_response_wrapper(
- spending_limit.create,
+ self.create = ( # pyright: ignore[reportDeprecated]
+ to_streamed_response_wrapper(
+ spending_limit.create, # pyright: ignore[reportDeprecated],
+ )
)
self.delete = to_streamed_response_wrapper(
spending_limit.delete,
@@ -372,8 +387,10 @@ class AsyncSpendingLimitResourceWithStreamingResponse:
def __init__(self, spending_limit: AsyncSpendingLimitResource) -> None:
self._spending_limit = spending_limit
- self.create = async_to_streamed_response_wrapper(
- spending_limit.create,
+ self.create = ( # pyright: ignore[reportDeprecated]
+ async_to_streamed_response_wrapper(
+ spending_limit.create, # pyright: ignore[reportDeprecated],
+ )
)
self.delete = async_to_streamed_response_wrapper(
spending_limit.delete,
diff --git a/src/cloudflare/resources/email_auth/__init__.py b/src/cloudflare/resources/email_auth/__init__.py
new file mode 100644
index 00000000000..92bb375f807
--- /dev/null
+++ b/src/cloudflare/resources/email_auth/__init__.py
@@ -0,0 +1,47 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .spf import (
+ SPFResource,
+ AsyncSPFResource,
+ SPFResourceWithRawResponse,
+ AsyncSPFResourceWithRawResponse,
+ SPFResourceWithStreamingResponse,
+ AsyncSPFResourceWithStreamingResponse,
+)
+from .email_auth import (
+ EmailAuthResource,
+ AsyncEmailAuthResource,
+ EmailAuthResourceWithRawResponse,
+ AsyncEmailAuthResourceWithRawResponse,
+ EmailAuthResourceWithStreamingResponse,
+ AsyncEmailAuthResourceWithStreamingResponse,
+)
+from .dmarc_reports import (
+ DMARCReportsResource,
+ AsyncDMARCReportsResource,
+ DMARCReportsResourceWithRawResponse,
+ AsyncDMARCReportsResourceWithRawResponse,
+ DMARCReportsResourceWithStreamingResponse,
+ AsyncDMARCReportsResourceWithStreamingResponse,
+)
+
+__all__ = [
+ "DMARCReportsResource",
+ "AsyncDMARCReportsResource",
+ "DMARCReportsResourceWithRawResponse",
+ "AsyncDMARCReportsResourceWithRawResponse",
+ "DMARCReportsResourceWithStreamingResponse",
+ "AsyncDMARCReportsResourceWithStreamingResponse",
+ "SPFResource",
+ "AsyncSPFResource",
+ "SPFResourceWithRawResponse",
+ "AsyncSPFResourceWithRawResponse",
+ "SPFResourceWithStreamingResponse",
+ "AsyncSPFResourceWithStreamingResponse",
+ "EmailAuthResource",
+ "AsyncEmailAuthResource",
+ "EmailAuthResourceWithRawResponse",
+ "AsyncEmailAuthResourceWithRawResponse",
+ "EmailAuthResourceWithStreamingResponse",
+ "AsyncEmailAuthResourceWithStreamingResponse",
+]
diff --git a/src/cloudflare/resources/email_auth/api.md b/src/cloudflare/resources/email_auth/api.md
new file mode 100644
index 00000000000..08ac48bd434
--- /dev/null
+++ b/src/cloudflare/resources/email_auth/api.md
@@ -0,0 +1,28 @@
+# EmailAuth
+
+## DMARCReports
+
+Types:
+
+```python
+from cloudflare.types.email_auth import DMARCReportEditResponse, DMARCReportGetResponse
+```
+
+Methods:
+
+- client.email_auth.dmarc_reports.edit(\*, zone_id, \*\*params) -> Optional[DMARCReportEditResponse]
+- client.email_auth.dmarc_reports.get(\*, zone_id) -> Optional[DMARCReportGetResponse]
+
+## SPF
+
+### Inspect
+
+Types:
+
+```python
+from cloudflare.types.email_auth.spf import InspectGetResponse
+```
+
+Methods:
+
+- client.email_auth.spf.inspect.get(\*, zone_id, \*\*params) -> Optional[InspectGetResponse]
diff --git a/src/cloudflare/resources/email_auth/dmarc_reports.py b/src/cloudflare/resources/email_auth/dmarc_reports.py
new file mode 100644
index 00000000000..a5cc3ae70d7
--- /dev/null
+++ b/src/cloudflare/resources/email_auth/dmarc_reports.py
@@ -0,0 +1,307 @@
+# 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, 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 ..._wrappers import ResultWrapper
+from ..._base_client import make_request_options
+from ...types.email_auth import dmarc_report_edit_params
+from ...types.email_auth.dmarc_report_get_response import DMARCReportGetResponse
+from ...types.email_auth.dmarc_report_edit_response import DMARCReportEditResponse
+
+__all__ = ["DMARCReportsResource", "AsyncDMARCReportsResource"]
+
+
+class DMARCReportsResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> DMARCReportsResourceWithRawResponse:
+ """
+ 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 DMARCReportsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> DMARCReportsResourceWithStreamingResponse:
+ """
+ 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 DMARCReportsResourceWithStreamingResponse(self)
+
+ def edit(
+ self,
+ *,
+ zone_id: str,
+ enabled: Optional[bool] | Omit = omit,
+ skip_wizard: Optional[bool] | 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,
+ ) -> Optional[DMARCReportEditResponse]:
+ """Updates the DMARC report configuration for a zone.
+
+ At least one of `enabled` or
+ `skip_wizard` must be provided. When enabling, the handler will ensure the DMARC
+ RUA record exists in DNS.
+
+ Args:
+ zone_id: Identifier.
+
+ enabled: Enable or disable DMARC reports for this zone
+
+ skip_wizard: Skip the DMARC setup wizard
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not zone_id:
+ raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
+ return self._patch(
+ path_template("/zones/{zone_id}/email/auth/dmarc-reports", zone_id=zone_id),
+ body=maybe_transform(
+ {
+ "enabled": enabled,
+ "skip_wizard": skip_wizard,
+ },
+ dmarc_report_edit_params.DMARCReportEditParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ post_parser=ResultWrapper[Optional[DMARCReportEditResponse]]._unwrapper,
+ ),
+ cast_to=cast(Type[Optional[DMARCReportEditResponse]], ResultWrapper[DMARCReportEditResponse]),
+ )
+
+ def get(
+ self,
+ *,
+ zone_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[DMARCReportGetResponse]:
+ """Retrieves the current DMARC report configuration and status for a zone.
+
+ Returns
+ the RUA prefix, enabled status, approved sources, and DNS records.
+
+ Args:
+ zone_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 zone_id:
+ raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
+ return self._get(
+ path_template("/zones/{zone_id}/email/auth/dmarc-reports", zone_id=zone_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ post_parser=ResultWrapper[Optional[DMARCReportGetResponse]]._unwrapper,
+ ),
+ cast_to=cast(Type[Optional[DMARCReportGetResponse]], ResultWrapper[DMARCReportGetResponse]),
+ )
+
+
+class AsyncDMARCReportsResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncDMARCReportsResourceWithRawResponse:
+ """
+ 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 AsyncDMARCReportsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncDMARCReportsResourceWithStreamingResponse:
+ """
+ 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 AsyncDMARCReportsResourceWithStreamingResponse(self)
+
+ async def edit(
+ self,
+ *,
+ zone_id: str,
+ enabled: Optional[bool] | Omit = omit,
+ skip_wizard: Optional[bool] | 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,
+ ) -> Optional[DMARCReportEditResponse]:
+ """Updates the DMARC report configuration for a zone.
+
+ At least one of `enabled` or
+ `skip_wizard` must be provided. When enabling, the handler will ensure the DMARC
+ RUA record exists in DNS.
+
+ Args:
+ zone_id: Identifier.
+
+ enabled: Enable or disable DMARC reports for this zone
+
+ skip_wizard: Skip the DMARC setup wizard
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not zone_id:
+ raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
+ return await self._patch(
+ path_template("/zones/{zone_id}/email/auth/dmarc-reports", zone_id=zone_id),
+ body=await async_maybe_transform(
+ {
+ "enabled": enabled,
+ "skip_wizard": skip_wizard,
+ },
+ dmarc_report_edit_params.DMARCReportEditParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ post_parser=ResultWrapper[Optional[DMARCReportEditResponse]]._unwrapper,
+ ),
+ cast_to=cast(Type[Optional[DMARCReportEditResponse]], ResultWrapper[DMARCReportEditResponse]),
+ )
+
+ async def get(
+ self,
+ *,
+ zone_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[DMARCReportGetResponse]:
+ """Retrieves the current DMARC report configuration and status for a zone.
+
+ Returns
+ the RUA prefix, enabled status, approved sources, and DNS records.
+
+ Args:
+ zone_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 zone_id:
+ raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
+ return await self._get(
+ path_template("/zones/{zone_id}/email/auth/dmarc-reports", zone_id=zone_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ post_parser=ResultWrapper[Optional[DMARCReportGetResponse]]._unwrapper,
+ ),
+ cast_to=cast(Type[Optional[DMARCReportGetResponse]], ResultWrapper[DMARCReportGetResponse]),
+ )
+
+
+class DMARCReportsResourceWithRawResponse:
+ def __init__(self, dmarc_reports: DMARCReportsResource) -> None:
+ self._dmarc_reports = dmarc_reports
+
+ self.edit = to_raw_response_wrapper(
+ dmarc_reports.edit,
+ )
+ self.get = to_raw_response_wrapper(
+ dmarc_reports.get,
+ )
+
+
+class AsyncDMARCReportsResourceWithRawResponse:
+ def __init__(self, dmarc_reports: AsyncDMARCReportsResource) -> None:
+ self._dmarc_reports = dmarc_reports
+
+ self.edit = async_to_raw_response_wrapper(
+ dmarc_reports.edit,
+ )
+ self.get = async_to_raw_response_wrapper(
+ dmarc_reports.get,
+ )
+
+
+class DMARCReportsResourceWithStreamingResponse:
+ def __init__(self, dmarc_reports: DMARCReportsResource) -> None:
+ self._dmarc_reports = dmarc_reports
+
+ self.edit = to_streamed_response_wrapper(
+ dmarc_reports.edit,
+ )
+ self.get = to_streamed_response_wrapper(
+ dmarc_reports.get,
+ )
+
+
+class AsyncDMARCReportsResourceWithStreamingResponse:
+ def __init__(self, dmarc_reports: AsyncDMARCReportsResource) -> None:
+ self._dmarc_reports = dmarc_reports
+
+ self.edit = async_to_streamed_response_wrapper(
+ dmarc_reports.edit,
+ )
+ self.get = async_to_streamed_response_wrapper(
+ dmarc_reports.get,
+ )
diff --git a/src/cloudflare/resources/email_auth/email_auth.py b/src/cloudflare/resources/email_auth/email_auth.py
new file mode 100644
index 00000000000..9a9e4be4906
--- /dev/null
+++ b/src/cloudflare/resources/email_auth/email_auth.py
@@ -0,0 +1,134 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .spf.spf import (
+ SPFResource,
+ AsyncSPFResource,
+ SPFResourceWithRawResponse,
+ AsyncSPFResourceWithRawResponse,
+ SPFResourceWithStreamingResponse,
+ AsyncSPFResourceWithStreamingResponse,
+)
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from .dmarc_reports import (
+ DMARCReportsResource,
+ AsyncDMARCReportsResource,
+ DMARCReportsResourceWithRawResponse,
+ AsyncDMARCReportsResourceWithRawResponse,
+ DMARCReportsResourceWithStreamingResponse,
+ AsyncDMARCReportsResourceWithStreamingResponse,
+)
+
+__all__ = ["EmailAuthResource", "AsyncEmailAuthResource"]
+
+
+class EmailAuthResource(SyncAPIResource):
+ @cached_property
+ def dmarc_reports(self) -> DMARCReportsResource:
+ return DMARCReportsResource(self._client)
+
+ @cached_property
+ def spf(self) -> SPFResource:
+ return SPFResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> EmailAuthResourceWithRawResponse:
+ """
+ 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 EmailAuthResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> EmailAuthResourceWithStreamingResponse:
+ """
+ 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 EmailAuthResourceWithStreamingResponse(self)
+
+
+class AsyncEmailAuthResource(AsyncAPIResource):
+ @cached_property
+ def dmarc_reports(self) -> AsyncDMARCReportsResource:
+ return AsyncDMARCReportsResource(self._client)
+
+ @cached_property
+ def spf(self) -> AsyncSPFResource:
+ return AsyncSPFResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncEmailAuthResourceWithRawResponse:
+ """
+ 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 AsyncEmailAuthResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncEmailAuthResourceWithStreamingResponse:
+ """
+ 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 AsyncEmailAuthResourceWithStreamingResponse(self)
+
+
+class EmailAuthResourceWithRawResponse:
+ def __init__(self, email_auth: EmailAuthResource) -> None:
+ self._email_auth = email_auth
+
+ @cached_property
+ def dmarc_reports(self) -> DMARCReportsResourceWithRawResponse:
+ return DMARCReportsResourceWithRawResponse(self._email_auth.dmarc_reports)
+
+ @cached_property
+ def spf(self) -> SPFResourceWithRawResponse:
+ return SPFResourceWithRawResponse(self._email_auth.spf)
+
+
+class AsyncEmailAuthResourceWithRawResponse:
+ def __init__(self, email_auth: AsyncEmailAuthResource) -> None:
+ self._email_auth = email_auth
+
+ @cached_property
+ def dmarc_reports(self) -> AsyncDMARCReportsResourceWithRawResponse:
+ return AsyncDMARCReportsResourceWithRawResponse(self._email_auth.dmarc_reports)
+
+ @cached_property
+ def spf(self) -> AsyncSPFResourceWithRawResponse:
+ return AsyncSPFResourceWithRawResponse(self._email_auth.spf)
+
+
+class EmailAuthResourceWithStreamingResponse:
+ def __init__(self, email_auth: EmailAuthResource) -> None:
+ self._email_auth = email_auth
+
+ @cached_property
+ def dmarc_reports(self) -> DMARCReportsResourceWithStreamingResponse:
+ return DMARCReportsResourceWithStreamingResponse(self._email_auth.dmarc_reports)
+
+ @cached_property
+ def spf(self) -> SPFResourceWithStreamingResponse:
+ return SPFResourceWithStreamingResponse(self._email_auth.spf)
+
+
+class AsyncEmailAuthResourceWithStreamingResponse:
+ def __init__(self, email_auth: AsyncEmailAuthResource) -> None:
+ self._email_auth = email_auth
+
+ @cached_property
+ def dmarc_reports(self) -> AsyncDMARCReportsResourceWithStreamingResponse:
+ return AsyncDMARCReportsResourceWithStreamingResponse(self._email_auth.dmarc_reports)
+
+ @cached_property
+ def spf(self) -> AsyncSPFResourceWithStreamingResponse:
+ return AsyncSPFResourceWithStreamingResponse(self._email_auth.spf)
diff --git a/src/cloudflare/resources/email_auth/spf/__init__.py b/src/cloudflare/resources/email_auth/spf/__init__.py
new file mode 100644
index 00000000000..a4cb6ce4d95
--- /dev/null
+++ b/src/cloudflare/resources/email_auth/spf/__init__.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .spf import (
+ SPFResource,
+ AsyncSPFResource,
+ SPFResourceWithRawResponse,
+ AsyncSPFResourceWithRawResponse,
+ SPFResourceWithStreamingResponse,
+ AsyncSPFResourceWithStreamingResponse,
+)
+from .inspect import (
+ InspectResource,
+ AsyncInspectResource,
+ InspectResourceWithRawResponse,
+ AsyncInspectResourceWithRawResponse,
+ InspectResourceWithStreamingResponse,
+ AsyncInspectResourceWithStreamingResponse,
+)
+
+__all__ = [
+ "InspectResource",
+ "AsyncInspectResource",
+ "InspectResourceWithRawResponse",
+ "AsyncInspectResourceWithRawResponse",
+ "InspectResourceWithStreamingResponse",
+ "AsyncInspectResourceWithStreamingResponse",
+ "SPFResource",
+ "AsyncSPFResource",
+ "SPFResourceWithRawResponse",
+ "AsyncSPFResourceWithRawResponse",
+ "SPFResourceWithStreamingResponse",
+ "AsyncSPFResourceWithStreamingResponse",
+]
diff --git a/src/cloudflare/resources/email_auth/spf/inspect.py b/src/cloudflare/resources/email_auth/spf/inspect.py
new file mode 100644
index 00000000000..8d52113efe9
--- /dev/null
+++ b/src/cloudflare/resources/email_auth/spf/inspect.py
@@ -0,0 +1,208 @@
+# 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, 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 ...._base_client import make_request_options
+from ....types.email_auth.spf import inspect_get_params
+from ....types.email_auth.spf.inspect_get_response import InspectGetResponse
+
+__all__ = ["InspectResource", "AsyncInspectResource"]
+
+
+class InspectResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> InspectResourceWithRawResponse:
+ """
+ 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 InspectResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> InspectResourceWithStreamingResponse:
+ """
+ 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 InspectResourceWithStreamingResponse(self)
+
+ def get(
+ self,
+ *,
+ zone_id: str,
+ 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[InspectGetResponse]:
+ """
+ Inspects a specific SPF TXT record and returns a parsed tree structure in the
+ spflimit-worker format.
+
+ The record ID must be provided via the `id` query parameter.
+
+ Returns a recursive tree showing:
+
+ - Parsed components with their qualifiers and types
+ - Nested includes recursively resolved within components
+ - Per-component and total lookup counts
+ - Detailed error information with context
+
+ Args:
+ zone_id: Identifier.
+
+ id: DNS record ID (rec_tag) to inspect
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not zone_id:
+ raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
+ return self._get(
+ path_template("/zones/{zone_id}/email/auth/spf/inspect", zone_id=zone_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"id": id}, inspect_get_params.InspectGetParams),
+ post_parser=ResultWrapper[Optional[InspectGetResponse]]._unwrapper,
+ ),
+ cast_to=cast(Type[Optional[InspectGetResponse]], ResultWrapper[InspectGetResponse]),
+ )
+
+
+class AsyncInspectResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncInspectResourceWithRawResponse:
+ """
+ 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 AsyncInspectResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncInspectResourceWithStreamingResponse:
+ """
+ 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 AsyncInspectResourceWithStreamingResponse(self)
+
+ async def get(
+ self,
+ *,
+ zone_id: str,
+ 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[InspectGetResponse]:
+ """
+ Inspects a specific SPF TXT record and returns a parsed tree structure in the
+ spflimit-worker format.
+
+ The record ID must be provided via the `id` query parameter.
+
+ Returns a recursive tree showing:
+
+ - Parsed components with their qualifiers and types
+ - Nested includes recursively resolved within components
+ - Per-component and total lookup counts
+ - Detailed error information with context
+
+ Args:
+ zone_id: Identifier.
+
+ id: DNS record ID (rec_tag) to inspect
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not zone_id:
+ raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
+ return await self._get(
+ path_template("/zones/{zone_id}/email/auth/spf/inspect", zone_id=zone_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform({"id": id}, inspect_get_params.InspectGetParams),
+ post_parser=ResultWrapper[Optional[InspectGetResponse]]._unwrapper,
+ ),
+ cast_to=cast(Type[Optional[InspectGetResponse]], ResultWrapper[InspectGetResponse]),
+ )
+
+
+class InspectResourceWithRawResponse:
+ def __init__(self, inspect: InspectResource) -> None:
+ self._inspect = inspect
+
+ self.get = to_raw_response_wrapper(
+ inspect.get,
+ )
+
+
+class AsyncInspectResourceWithRawResponse:
+ def __init__(self, inspect: AsyncInspectResource) -> None:
+ self._inspect = inspect
+
+ self.get = async_to_raw_response_wrapper(
+ inspect.get,
+ )
+
+
+class InspectResourceWithStreamingResponse:
+ def __init__(self, inspect: InspectResource) -> None:
+ self._inspect = inspect
+
+ self.get = to_streamed_response_wrapper(
+ inspect.get,
+ )
+
+
+class AsyncInspectResourceWithStreamingResponse:
+ def __init__(self, inspect: AsyncInspectResource) -> None:
+ self._inspect = inspect
+
+ self.get = async_to_streamed_response_wrapper(
+ inspect.get,
+ )
diff --git a/src/cloudflare/resources/email_auth/spf/spf.py b/src/cloudflare/resources/email_auth/spf/spf.py
new file mode 100644
index 00000000000..ef37938656b
--- /dev/null
+++ b/src/cloudflare/resources/email_auth/spf/spf.py
@@ -0,0 +1,102 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .inspect import (
+ InspectResource,
+ AsyncInspectResource,
+ InspectResourceWithRawResponse,
+ AsyncInspectResourceWithRawResponse,
+ InspectResourceWithStreamingResponse,
+ AsyncInspectResourceWithStreamingResponse,
+)
+from ...._compat import cached_property
+from ...._resource import SyncAPIResource, AsyncAPIResource
+
+__all__ = ["SPFResource", "AsyncSPFResource"]
+
+
+class SPFResource(SyncAPIResource):
+ @cached_property
+ def inspect(self) -> InspectResource:
+ return InspectResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> SPFResourceWithRawResponse:
+ """
+ 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 SPFResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> SPFResourceWithStreamingResponse:
+ """
+ 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 SPFResourceWithStreamingResponse(self)
+
+
+class AsyncSPFResource(AsyncAPIResource):
+ @cached_property
+ def inspect(self) -> AsyncInspectResource:
+ return AsyncInspectResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncSPFResourceWithRawResponse:
+ """
+ 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 AsyncSPFResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncSPFResourceWithStreamingResponse:
+ """
+ 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 AsyncSPFResourceWithStreamingResponse(self)
+
+
+class SPFResourceWithRawResponse:
+ def __init__(self, spf: SPFResource) -> None:
+ self._spf = spf
+
+ @cached_property
+ def inspect(self) -> InspectResourceWithRawResponse:
+ return InspectResourceWithRawResponse(self._spf.inspect)
+
+
+class AsyncSPFResourceWithRawResponse:
+ def __init__(self, spf: AsyncSPFResource) -> None:
+ self._spf = spf
+
+ @cached_property
+ def inspect(self) -> AsyncInspectResourceWithRawResponse:
+ return AsyncInspectResourceWithRawResponse(self._spf.inspect)
+
+
+class SPFResourceWithStreamingResponse:
+ def __init__(self, spf: SPFResource) -> None:
+ self._spf = spf
+
+ @cached_property
+ def inspect(self) -> InspectResourceWithStreamingResponse:
+ return InspectResourceWithStreamingResponse(self._spf.inspect)
+
+
+class AsyncSPFResourceWithStreamingResponse:
+ def __init__(self, spf: AsyncSPFResource) -> None:
+ self._spf = spf
+
+ @cached_property
+ def inspect(self) -> AsyncInspectResourceWithStreamingResponse:
+ return AsyncInspectResourceWithStreamingResponse(self._spf.inspect)
diff --git a/src/cloudflare/resources/email_routing/api.md b/src/cloudflare/resources/email_routing/api.md
index 26a1d76019a..60551e95244 100644
--- a/src/cloudflare/resources/email_routing/api.md
+++ b/src/cloudflare/resources/email_routing/api.md
@@ -39,7 +39,6 @@ Methods:
- client.email_routing.rules.create(\*, zone_id, \*\*params) -> Optional[EmailRoutingRule]
- client.email_routing.rules.update(rule_identifier, \*, zone_id, \*\*params) -> Optional[EmailRoutingRule]
-- client.email_routing.rules.list(\*, zone_id, \*\*params) -> SyncV4PagePaginationArray[EmailRoutingRule]
- client.email_routing.rules.delete(rule_identifier, \*, zone_id) -> Optional[EmailRoutingRule]
- client.email_routing.rules.get(rule_identifier, \*, zone_id) -> Optional[EmailRoutingRule]
diff --git a/src/cloudflare/resources/email_routing/rules/rules.py b/src/cloudflare/resources/email_routing/rules/rules.py
index b38ce74c7a0..cff2f977c39 100644
--- a/src/cloudflare/resources/email_routing/rules/rules.py
+++ b/src/cloudflare/resources/email_routing/rules/rules.py
@@ -26,9 +26,8 @@
async_to_streamed_response_wrapper,
)
from ...._wrappers import ResultWrapper
-from ....pagination import SyncV4PagePaginationArray, AsyncV4PagePaginationArray
-from ...._base_client import AsyncPaginator, make_request_options
-from ....types.email_routing import rule_list_params, rule_create_params, rule_update_params
+from ...._base_client import make_request_options
+from ....types.email_routing import rule_create_params, rule_update_params
from ....types.email_routing.action_param import ActionParam
from ....types.email_routing.matcher_param import MatcherParam
from ....types.email_routing.email_routing_rule import EmailRoutingRule
@@ -202,62 +201,6 @@ def update(
cast_to=cast(Type[Optional[EmailRoutingRule]], ResultWrapper[EmailRoutingRule]),
)
- def list(
- self,
- *,
- zone_id: str,
- enabled: Literal[True, False] | Omit = omit,
- page: float | Omit = omit,
- per_page: float | 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[EmailRoutingRule]:
- """
- Lists existing routing rules.
-
- Args:
- zone_id: Identifier.
-
- enabled: Filter by enabled routing rules.
-
- page: Page number of paginated results.
-
- per_page: Maximum number of results 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 zone_id:
- raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
- return self._get_api_list(
- path_template("/zones/{zone_id}/email/routing/rules", zone_id=zone_id),
- page=SyncV4PagePaginationArray[EmailRoutingRule],
- options=make_request_options(
- extra_headers=extra_headers,
- extra_query=extra_query,
- extra_body=extra_body,
- timeout=timeout,
- query=maybe_transform(
- {
- "enabled": enabled,
- "page": page,
- "per_page": per_page,
- },
- rule_list_params.RuleListParams,
- ),
- ),
- model=EmailRoutingRule,
- )
-
def delete(
self,
rule_identifier: str,
@@ -521,62 +464,6 @@ async def update(
cast_to=cast(Type[Optional[EmailRoutingRule]], ResultWrapper[EmailRoutingRule]),
)
- def list(
- self,
- *,
- zone_id: str,
- enabled: Literal[True, False] | Omit = omit,
- page: float | Omit = omit,
- per_page: float | 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[EmailRoutingRule, AsyncV4PagePaginationArray[EmailRoutingRule]]:
- """
- Lists existing routing rules.
-
- Args:
- zone_id: Identifier.
-
- enabled: Filter by enabled routing rules.
-
- page: Page number of paginated results.
-
- per_page: Maximum number of results 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 zone_id:
- raise ValueError(f"Expected a non-empty value for `zone_id` but received {zone_id!r}")
- return self._get_api_list(
- path_template("/zones/{zone_id}/email/routing/rules", zone_id=zone_id),
- page=AsyncV4PagePaginationArray[EmailRoutingRule],
- options=make_request_options(
- extra_headers=extra_headers,
- extra_query=extra_query,
- extra_body=extra_body,
- timeout=timeout,
- query=maybe_transform(
- {
- "enabled": enabled,
- "page": page,
- "per_page": per_page,
- },
- rule_list_params.RuleListParams,
- ),
- ),
- model=EmailRoutingRule,
- )
-
async def delete(
self,
rule_identifier: str,
@@ -684,9 +571,6 @@ def __init__(self, rules: RulesResource) -> None:
self.update = to_raw_response_wrapper(
rules.update,
)
- self.list = to_raw_response_wrapper(
- rules.list,
- )
self.delete = to_raw_response_wrapper(
rules.delete,
)
@@ -709,9 +593,6 @@ def __init__(self, rules: AsyncRulesResource) -> None:
self.update = async_to_raw_response_wrapper(
rules.update,
)
- self.list = async_to_raw_response_wrapper(
- rules.list,
- )
self.delete = async_to_raw_response_wrapper(
rules.delete,
)
@@ -734,9 +615,6 @@ def __init__(self, rules: RulesResource) -> None:
self.update = to_streamed_response_wrapper(
rules.update,
)
- self.list = to_streamed_response_wrapper(
- rules.list,
- )
self.delete = to_streamed_response_wrapper(
rules.delete,
)
@@ -759,9 +637,6 @@ def __init__(self, rules: AsyncRulesResource) -> None:
self.update = async_to_streamed_response_wrapper(
rules.update,
)
- self.list = async_to_streamed_response_wrapper(
- rules.list,
- )
self.delete = async_to_streamed_response_wrapper(
rules.delete,
)
diff --git a/src/cloudflare/resources/email_security/investigate/move.py b/src/cloudflare/resources/email_security/investigate/move.py
index 8fa530641c6..d23739406cb 100644
--- a/src/cloudflare/resources/email_security/investigate/move.py
+++ b/src/cloudflare/resources/email_security/investigate/move.py
@@ -53,6 +53,19 @@ def create(
destination: Literal[
"Inbox", "JunkEmail", "DeletedItems", "RecoverableItemsDeletions", "RecoverableItemsPurges"
],
+ expected_disposition: Literal[
+ "MALICIOUS",
+ "MALICIOUS-BEC",
+ "SUSPICIOUS",
+ "SPOOF",
+ "SPAM",
+ "BULK",
+ "ENCRYPTED",
+ "EXTERNAL",
+ "UNKNOWN",
+ "NONE",
+ ]
+ | 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,
@@ -89,7 +102,13 @@ def create(
investigate_id=investigate_id,
),
page=SyncSinglePage[MoveCreateResponse],
- body=maybe_transform({"destination": destination}, move_create_params.MoveCreateParams),
+ body=maybe_transform(
+ {
+ "destination": destination,
+ "expected_disposition": expected_disposition,
+ },
+ move_create_params.MoveCreateParams,
+ ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -104,6 +123,19 @@ def bulk(
destination: Literal[
"Inbox", "JunkEmail", "DeletedItems", "RecoverableItemsDeletions", "RecoverableItemsPurges"
],
+ expected_disposition: Literal[
+ "MALICIOUS",
+ "MALICIOUS-BEC",
+ "SUSPICIOUS",
+ "SPOOF",
+ "SPAM",
+ "BULK",
+ "ENCRYPTED",
+ "EXTERNAL",
+ "UNKNOWN",
+ "NONE",
+ ]
+ | Omit = omit,
ids: SequenceNotStr[str] | Omit = omit,
postfix_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.
@@ -142,6 +174,7 @@ def bulk(
body=maybe_transform(
{
"destination": destination,
+ "expected_disposition": expected_disposition,
"ids": ids,
"postfix_ids": postfix_ids,
},
@@ -183,6 +216,19 @@ def create(
destination: Literal[
"Inbox", "JunkEmail", "DeletedItems", "RecoverableItemsDeletions", "RecoverableItemsPurges"
],
+ expected_disposition: Literal[
+ "MALICIOUS",
+ "MALICIOUS-BEC",
+ "SUSPICIOUS",
+ "SPOOF",
+ "SPAM",
+ "BULK",
+ "ENCRYPTED",
+ "EXTERNAL",
+ "UNKNOWN",
+ "NONE",
+ ]
+ | 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,
@@ -219,7 +265,13 @@ def create(
investigate_id=investigate_id,
),
page=AsyncSinglePage[MoveCreateResponse],
- body=maybe_transform({"destination": destination}, move_create_params.MoveCreateParams),
+ body=maybe_transform(
+ {
+ "destination": destination,
+ "expected_disposition": expected_disposition,
+ },
+ move_create_params.MoveCreateParams,
+ ),
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
@@ -234,6 +286,19 @@ def bulk(
destination: Literal[
"Inbox", "JunkEmail", "DeletedItems", "RecoverableItemsDeletions", "RecoverableItemsPurges"
],
+ expected_disposition: Literal[
+ "MALICIOUS",
+ "MALICIOUS-BEC",
+ "SUSPICIOUS",
+ "SPOOF",
+ "SPAM",
+ "BULK",
+ "ENCRYPTED",
+ "EXTERNAL",
+ "UNKNOWN",
+ "NONE",
+ ]
+ | Omit = omit,
ids: SequenceNotStr[str] | Omit = omit,
postfix_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.
@@ -272,6 +337,7 @@ def bulk(
body=maybe_transform(
{
"destination": destination,
+ "expected_disposition": expected_disposition,
"ids": ids,
"postfix_ids": postfix_ids,
},
diff --git a/src/cloudflare/resources/moq/__init__.py b/src/cloudflare/resources/moq/__init__.py
new file mode 100644
index 00000000000..8b3e963eae5
--- /dev/null
+++ b/src/cloudflare/resources/moq/__init__.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .moq import (
+ MoQResource,
+ AsyncMoQResource,
+ MoQResourceWithRawResponse,
+ AsyncMoQResourceWithRawResponse,
+ MoQResourceWithStreamingResponse,
+ AsyncMoQResourceWithStreamingResponse,
+)
+from .relays import (
+ RelaysResource,
+ AsyncRelaysResource,
+ RelaysResourceWithRawResponse,
+ AsyncRelaysResourceWithRawResponse,
+ RelaysResourceWithStreamingResponse,
+ AsyncRelaysResourceWithStreamingResponse,
+)
+
+__all__ = [
+ "RelaysResource",
+ "AsyncRelaysResource",
+ "RelaysResourceWithRawResponse",
+ "AsyncRelaysResourceWithRawResponse",
+ "RelaysResourceWithStreamingResponse",
+ "AsyncRelaysResourceWithStreamingResponse",
+ "MoQResource",
+ "AsyncMoQResource",
+ "MoQResourceWithRawResponse",
+ "AsyncMoQResourceWithRawResponse",
+ "MoQResourceWithStreamingResponse",
+ "AsyncMoQResourceWithStreamingResponse",
+]
diff --git a/src/cloudflare/resources/moq/api.md b/src/cloudflare/resources/moq/api.md
new file mode 100644
index 00000000000..434c6a9d326
--- /dev/null
+++ b/src/cloudflare/resources/moq/api.md
@@ -0,0 +1,34 @@
+# MoQ
+
+## Relays
+
+Types:
+
+```python
+from cloudflare.types.moq import (
+ RelayCreateResponse,
+ RelayUpdateResponse,
+ RelayListResponse,
+ RelayGetResponse,
+)
+```
+
+Methods:
+
+- client.moq.relays.create(\*, account_id, \*\*params) -> Optional[RelayCreateResponse]
+- client.moq.relays.update(relay_id, \*, account_id, \*\*params) -> Optional[RelayUpdateResponse]
+- client.moq.relays.list(\*, account_id, \*\*params) -> SyncSinglePage[RelayListResponse]
+- client.moq.relays.delete(relay_id, \*, account_id) -> object
+- client.moq.relays.get(relay_id, \*, account_id) -> Optional[RelayGetResponse]
+
+### Tokens
+
+Types:
+
+```python
+from cloudflare.types.moq.relays import TokenRotateResponse
+```
+
+Methods:
+
+- client.moq.relays.tokens.rotate(relay_id, \*, account_id, \*\*params) -> Optional[TokenRotateResponse]
diff --git a/src/cloudflare/resources/moq/moq.py b/src/cloudflare/resources/moq/moq.py
new file mode 100644
index 00000000000..75102e3b192
--- /dev/null
+++ b/src/cloudflare/resources/moq/moq.py
@@ -0,0 +1,102 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from .relays.relays import (
+ RelaysResource,
+ AsyncRelaysResource,
+ RelaysResourceWithRawResponse,
+ AsyncRelaysResourceWithRawResponse,
+ RelaysResourceWithStreamingResponse,
+ AsyncRelaysResourceWithStreamingResponse,
+)
+
+__all__ = ["MoQResource", "AsyncMoQResource"]
+
+
+class MoQResource(SyncAPIResource):
+ @cached_property
+ def relays(self) -> RelaysResource:
+ return RelaysResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> MoQResourceWithRawResponse:
+ """
+ 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 MoQResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> MoQResourceWithStreamingResponse:
+ """
+ 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 MoQResourceWithStreamingResponse(self)
+
+
+class AsyncMoQResource(AsyncAPIResource):
+ @cached_property
+ def relays(self) -> AsyncRelaysResource:
+ return AsyncRelaysResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncMoQResourceWithRawResponse:
+ """
+ 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 AsyncMoQResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncMoQResourceWithStreamingResponse:
+ """
+ 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 AsyncMoQResourceWithStreamingResponse(self)
+
+
+class MoQResourceWithRawResponse:
+ def __init__(self, moq: MoQResource) -> None:
+ self._moq = moq
+
+ @cached_property
+ def relays(self) -> RelaysResourceWithRawResponse:
+ return RelaysResourceWithRawResponse(self._moq.relays)
+
+
+class AsyncMoQResourceWithRawResponse:
+ def __init__(self, moq: AsyncMoQResource) -> None:
+ self._moq = moq
+
+ @cached_property
+ def relays(self) -> AsyncRelaysResourceWithRawResponse:
+ return AsyncRelaysResourceWithRawResponse(self._moq.relays)
+
+
+class MoQResourceWithStreamingResponse:
+ def __init__(self, moq: MoQResource) -> None:
+ self._moq = moq
+
+ @cached_property
+ def relays(self) -> RelaysResourceWithStreamingResponse:
+ return RelaysResourceWithStreamingResponse(self._moq.relays)
+
+
+class AsyncMoQResourceWithStreamingResponse:
+ def __init__(self, moq: AsyncMoQResource) -> None:
+ self._moq = moq
+
+ @cached_property
+ def relays(self) -> AsyncRelaysResourceWithStreamingResponse:
+ return AsyncRelaysResourceWithStreamingResponse(self._moq.relays)
diff --git a/src/cloudflare/resources/moq/relays/__init__.py b/src/cloudflare/resources/moq/relays/__init__.py
new file mode 100644
index 00000000000..c69bba67272
--- /dev/null
+++ b/src/cloudflare/resources/moq/relays/__init__.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .relays import (
+ RelaysResource,
+ AsyncRelaysResource,
+ RelaysResourceWithRawResponse,
+ AsyncRelaysResourceWithRawResponse,
+ RelaysResourceWithStreamingResponse,
+ AsyncRelaysResourceWithStreamingResponse,
+)
+from .tokens import (
+ TokensResource,
+ AsyncTokensResource,
+ TokensResourceWithRawResponse,
+ AsyncTokensResourceWithRawResponse,
+ TokensResourceWithStreamingResponse,
+ AsyncTokensResourceWithStreamingResponse,
+)
+
+__all__ = [
+ "TokensResource",
+ "AsyncTokensResource",
+ "TokensResourceWithRawResponse",
+ "AsyncTokensResourceWithRawResponse",
+ "TokensResourceWithStreamingResponse",
+ "AsyncTokensResourceWithStreamingResponse",
+ "RelaysResource",
+ "AsyncRelaysResource",
+ "RelaysResourceWithRawResponse",
+ "AsyncRelaysResourceWithRawResponse",
+ "RelaysResourceWithStreamingResponse",
+ "AsyncRelaysResourceWithStreamingResponse",
+]
diff --git a/src/cloudflare/resources/moq/relays/relays.py b/src/cloudflare/resources/moq/relays/relays.py
new file mode 100644
index 00000000000..11eed213e54
--- /dev/null
+++ b/src/cloudflare/resources/moq/relays/relays.py
@@ -0,0 +1,709 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Type, Union, Optional, cast
+from datetime import datetime
+
+import httpx
+
+from .tokens import (
+ TokensResource,
+ AsyncTokensResource,
+ TokensResourceWithRawResponse,
+ AsyncTokensResourceWithRawResponse,
+ TokensResourceWithStreamingResponse,
+ AsyncTokensResourceWithStreamingResponse,
+)
+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 ...._wrappers import ResultWrapper
+from ....types.moq import relay_list_params, relay_create_params, relay_update_params
+from ....pagination import SyncSinglePage, AsyncSinglePage
+from ...._base_client import AsyncPaginator, make_request_options
+from ....types.moq.relay_get_response import RelayGetResponse
+from ....types.moq.relay_list_response import RelayListResponse
+from ....types.moq.relay_create_response import RelayCreateResponse
+from ....types.moq.relay_update_response import RelayUpdateResponse
+
+__all__ = ["RelaysResource", "AsyncRelaysResource"]
+
+
+class RelaysResource(SyncAPIResource):
+ @cached_property
+ def tokens(self) -> TokensResource:
+ return TokensResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> RelaysResourceWithRawResponse:
+ """
+ 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 RelaysResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> RelaysResourceWithStreamingResponse:
+ """
+ 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 RelaysResourceWithStreamingResponse(self)
+
+ def create(
+ self,
+ *,
+ account_id: str,
+ name: 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[RelayCreateResponse]:
+ """Provisions a new MoQ relay instance.
+
+ Auto-creates a publish+subscribe token and
+ a subscribe-only token. Token values are included in the response (shown once).
+ Config is set to defaults (lingering subscribe enabled, 30s ceiling, origin
+ fallback off). Use PUT to modify.
+
+ Args:
+ account_id: Cloudflare account identifier.
+
+ name: Human-readable name for the relay.
+
+ 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}/moq/relays", account_id=account_id),
+ body=maybe_transform({"name": name}, relay_create_params.RelayCreateParams),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ post_parser=ResultWrapper[Optional[RelayCreateResponse]]._unwrapper,
+ ),
+ cast_to=cast(Type[Optional[RelayCreateResponse]], ResultWrapper[RelayCreateResponse]),
+ )
+
+ def update(
+ self,
+ relay_id: str,
+ *,
+ account_id: str,
+ config: relay_update_params.Config | Omit = omit,
+ name: 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,
+ ) -> Optional[RelayUpdateResponse]:
+ """Updates a relay's name and/or configuration.
+
+ Partial updates: omitted fields are
+ preserved. Config sub-objects replace as whole objects when present.
+ origin_fallback and lingering_subscribe are mutually exclusive.
+
+ Args:
+ account_id: Cloudflare account identifier.
+
+ config: origin_fallback and lingering_subscribe are mutually exclusive.
+
+ 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 relay_id:
+ raise ValueError(f"Expected a non-empty value for `relay_id` but received {relay_id!r}")
+ return self._put(
+ path_template("/accounts/{account_id}/moq/relays/{relay_id}", account_id=account_id, relay_id=relay_id),
+ body=maybe_transform(
+ {
+ "config": config,
+ "name": name,
+ },
+ relay_update_params.RelayUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ post_parser=ResultWrapper[Optional[RelayUpdateResponse]]._unwrapper,
+ ),
+ cast_to=cast(Type[Optional[RelayUpdateResponse]], ResultWrapper[RelayUpdateResponse]),
+ )
+
+ def list(
+ self,
+ *,
+ account_id: str,
+ asc: bool | Omit = omit,
+ created_after: Union[str, datetime] | Omit = omit,
+ created_before: Union[str, datetime] | 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,
+ ) -> SyncSinglePage[RelayListResponse]:
+ """Lists all MoQ relays for the account.
+
+ Returns only metadata. Config, status, and
+ tokens are omitted.
+
+ Results are cursor-paginated (keyset on the `created` timestamp). Use
+ `created_before` / `created_after` with the `created` value of the first/last
+ item in a page to fetch the adjacent page. `result_info` reports the page
+ `count` and the `total` matching the cursor filters.
+
+ Args:
+ account_id: Cloudflare account identifier.
+
+ asc: Sort order by `created`. When true, results are returned oldest-first
+ (ascending); otherwise newest-first (descending, the default).
+
+ created_after: Cursor for pagination. Returns relays created strictly after this RFC 3339
+ timestamp (typically the `created` value of the last item on the current page,
+ to fetch the next page).
+
+ created_before: Cursor for pagination. Returns relays created strictly before this RFC 3339
+ timestamp (typically the `created` value of the first item on the current page,
+ to fetch the previous page).
+
+ per_page: Maximum number of relays 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}/moq/relays", account_id=account_id),
+ page=SyncSinglePage[RelayListResponse],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "asc": asc,
+ "created_after": created_after,
+ "created_before": created_before,
+ "per_page": per_page,
+ },
+ relay_list_params.RelayListParams,
+ ),
+ ),
+ model=RelayListResponse,
+ )
+
+ def delete(
+ self,
+ relay_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,
+ ) -> object:
+ """
+ Soft-deletes a MoQ relay.
+
+ Args:
+ account_id: Cloudflare account 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 relay_id:
+ raise ValueError(f"Expected a non-empty value for `relay_id` but received {relay_id!r}")
+ return self._delete(
+ path_template("/accounts/{account_id}/moq/relays/{relay_id}", account_id=account_id, relay_id=relay_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ post_parser=ResultWrapper[Optional[object]]._unwrapper,
+ ),
+ cast_to=cast(Type[object], ResultWrapper[object]),
+ )
+
+ def get(
+ self,
+ relay_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[RelayGetResponse]:
+ """Retrieves a single MoQ relay including config and status.
+
+ Tokens are NOT
+ included.
+
+ Args:
+ account_id: Cloudflare account 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 relay_id:
+ raise ValueError(f"Expected a non-empty value for `relay_id` but received {relay_id!r}")
+ return self._get(
+ path_template("/accounts/{account_id}/moq/relays/{relay_id}", account_id=account_id, relay_id=relay_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ post_parser=ResultWrapper[Optional[RelayGetResponse]]._unwrapper,
+ ),
+ cast_to=cast(Type[Optional[RelayGetResponse]], ResultWrapper[RelayGetResponse]),
+ )
+
+
+class AsyncRelaysResource(AsyncAPIResource):
+ @cached_property
+ def tokens(self) -> AsyncTokensResource:
+ return AsyncTokensResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncRelaysResourceWithRawResponse:
+ """
+ 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 AsyncRelaysResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncRelaysResourceWithStreamingResponse:
+ """
+ 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 AsyncRelaysResourceWithStreamingResponse(self)
+
+ async def create(
+ self,
+ *,
+ account_id: str,
+ name: 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[RelayCreateResponse]:
+ """Provisions a new MoQ relay instance.
+
+ Auto-creates a publish+subscribe token and
+ a subscribe-only token. Token values are included in the response (shown once).
+ Config is set to defaults (lingering subscribe enabled, 30s ceiling, origin
+ fallback off). Use PUT to modify.
+
+ Args:
+ account_id: Cloudflare account identifier.
+
+ name: Human-readable name for the relay.
+
+ 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}/moq/relays", account_id=account_id),
+ body=await async_maybe_transform({"name": name}, relay_create_params.RelayCreateParams),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ post_parser=ResultWrapper[Optional[RelayCreateResponse]]._unwrapper,
+ ),
+ cast_to=cast(Type[Optional[RelayCreateResponse]], ResultWrapper[RelayCreateResponse]),
+ )
+
+ async def update(
+ self,
+ relay_id: str,
+ *,
+ account_id: str,
+ config: relay_update_params.Config | Omit = omit,
+ name: 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,
+ ) -> Optional[RelayUpdateResponse]:
+ """Updates a relay's name and/or configuration.
+
+ Partial updates: omitted fields are
+ preserved. Config sub-objects replace as whole objects when present.
+ origin_fallback and lingering_subscribe are mutually exclusive.
+
+ Args:
+ account_id: Cloudflare account identifier.
+
+ config: origin_fallback and lingering_subscribe are mutually exclusive.
+
+ 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 relay_id:
+ raise ValueError(f"Expected a non-empty value for `relay_id` but received {relay_id!r}")
+ return await self._put(
+ path_template("/accounts/{account_id}/moq/relays/{relay_id}", account_id=account_id, relay_id=relay_id),
+ body=await async_maybe_transform(
+ {
+ "config": config,
+ "name": name,
+ },
+ relay_update_params.RelayUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ post_parser=ResultWrapper[Optional[RelayUpdateResponse]]._unwrapper,
+ ),
+ cast_to=cast(Type[Optional[RelayUpdateResponse]], ResultWrapper[RelayUpdateResponse]),
+ )
+
+ def list(
+ self,
+ *,
+ account_id: str,
+ asc: bool | Omit = omit,
+ created_after: Union[str, datetime] | Omit = omit,
+ created_before: Union[str, datetime] | 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[RelayListResponse, AsyncSinglePage[RelayListResponse]]:
+ """Lists all MoQ relays for the account.
+
+ Returns only metadata. Config, status, and
+ tokens are omitted.
+
+ Results are cursor-paginated (keyset on the `created` timestamp). Use
+ `created_before` / `created_after` with the `created` value of the first/last
+ item in a page to fetch the adjacent page. `result_info` reports the page
+ `count` and the `total` matching the cursor filters.
+
+ Args:
+ account_id: Cloudflare account identifier.
+
+ asc: Sort order by `created`. When true, results are returned oldest-first
+ (ascending); otherwise newest-first (descending, the default).
+
+ created_after: Cursor for pagination. Returns relays created strictly after this RFC 3339
+ timestamp (typically the `created` value of the last item on the current page,
+ to fetch the next page).
+
+ created_before: Cursor for pagination. Returns relays created strictly before this RFC 3339
+ timestamp (typically the `created` value of the first item on the current page,
+ to fetch the previous page).
+
+ per_page: Maximum number of relays 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}/moq/relays", account_id=account_id),
+ page=AsyncSinglePage[RelayListResponse],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "asc": asc,
+ "created_after": created_after,
+ "created_before": created_before,
+ "per_page": per_page,
+ },
+ relay_list_params.RelayListParams,
+ ),
+ ),
+ model=RelayListResponse,
+ )
+
+ async def delete(
+ self,
+ relay_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,
+ ) -> object:
+ """
+ Soft-deletes a MoQ relay.
+
+ Args:
+ account_id: Cloudflare account 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 relay_id:
+ raise ValueError(f"Expected a non-empty value for `relay_id` but received {relay_id!r}")
+ return await self._delete(
+ path_template("/accounts/{account_id}/moq/relays/{relay_id}", account_id=account_id, relay_id=relay_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ post_parser=ResultWrapper[Optional[object]]._unwrapper,
+ ),
+ cast_to=cast(Type[object], ResultWrapper[object]),
+ )
+
+ async def get(
+ self,
+ relay_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[RelayGetResponse]:
+ """Retrieves a single MoQ relay including config and status.
+
+ Tokens are NOT
+ included.
+
+ Args:
+ account_id: Cloudflare account 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 relay_id:
+ raise ValueError(f"Expected a non-empty value for `relay_id` but received {relay_id!r}")
+ return await self._get(
+ path_template("/accounts/{account_id}/moq/relays/{relay_id}", account_id=account_id, relay_id=relay_id),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ post_parser=ResultWrapper[Optional[RelayGetResponse]]._unwrapper,
+ ),
+ cast_to=cast(Type[Optional[RelayGetResponse]], ResultWrapper[RelayGetResponse]),
+ )
+
+
+class RelaysResourceWithRawResponse:
+ def __init__(self, relays: RelaysResource) -> None:
+ self._relays = relays
+
+ self.create = to_raw_response_wrapper(
+ relays.create,
+ )
+ self.update = to_raw_response_wrapper(
+ relays.update,
+ )
+ self.list = to_raw_response_wrapper(
+ relays.list,
+ )
+ self.delete = to_raw_response_wrapper(
+ relays.delete,
+ )
+ self.get = to_raw_response_wrapper(
+ relays.get,
+ )
+
+ @cached_property
+ def tokens(self) -> TokensResourceWithRawResponse:
+ return TokensResourceWithRawResponse(self._relays.tokens)
+
+
+class AsyncRelaysResourceWithRawResponse:
+ def __init__(self, relays: AsyncRelaysResource) -> None:
+ self._relays = relays
+
+ self.create = async_to_raw_response_wrapper(
+ relays.create,
+ )
+ self.update = async_to_raw_response_wrapper(
+ relays.update,
+ )
+ self.list = async_to_raw_response_wrapper(
+ relays.list,
+ )
+ self.delete = async_to_raw_response_wrapper(
+ relays.delete,
+ )
+ self.get = async_to_raw_response_wrapper(
+ relays.get,
+ )
+
+ @cached_property
+ def tokens(self) -> AsyncTokensResourceWithRawResponse:
+ return AsyncTokensResourceWithRawResponse(self._relays.tokens)
+
+
+class RelaysResourceWithStreamingResponse:
+ def __init__(self, relays: RelaysResource) -> None:
+ self._relays = relays
+
+ self.create = to_streamed_response_wrapper(
+ relays.create,
+ )
+ self.update = to_streamed_response_wrapper(
+ relays.update,
+ )
+ self.list = to_streamed_response_wrapper(
+ relays.list,
+ )
+ self.delete = to_streamed_response_wrapper(
+ relays.delete,
+ )
+ self.get = to_streamed_response_wrapper(
+ relays.get,
+ )
+
+ @cached_property
+ def tokens(self) -> TokensResourceWithStreamingResponse:
+ return TokensResourceWithStreamingResponse(self._relays.tokens)
+
+
+class AsyncRelaysResourceWithStreamingResponse:
+ def __init__(self, relays: AsyncRelaysResource) -> None:
+ self._relays = relays
+
+ self.create = async_to_streamed_response_wrapper(
+ relays.create,
+ )
+ self.update = async_to_streamed_response_wrapper(
+ relays.update,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ relays.list,
+ )
+ self.delete = async_to_streamed_response_wrapper(
+ relays.delete,
+ )
+ self.get = async_to_streamed_response_wrapper(
+ relays.get,
+ )
+
+ @cached_property
+ def tokens(self) -> AsyncTokensResourceWithStreamingResponse:
+ return AsyncTokensResourceWithStreamingResponse(self._relays.tokens)
diff --git a/src/cloudflare/resources/moq/relays/tokens.py b/src/cloudflare/resources/moq/relays/tokens.py
new file mode 100644
index 00000000000..c09937159b1
--- /dev/null
+++ b/src/cloudflare/resources/moq/relays/tokens.py
@@ -0,0 +1,203 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Type, Optional, cast
+from typing_extensions import Literal
+
+import httpx
+
+from ...._types import Body, Query, Headers, NotGiven, 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 ...._base_client import make_request_options
+from ....types.moq.relays import token_rotate_params
+from ....types.moq.relays.token_rotate_response import TokenRotateResponse
+
+__all__ = ["TokensResource", "AsyncTokensResource"]
+
+
+class TokensResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> TokensResourceWithRawResponse:
+ """
+ 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 TokensResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> TokensResourceWithStreamingResponse:
+ """
+ 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 TokensResourceWithStreamingResponse(self)
+
+ def rotate(
+ self,
+ relay_id: str,
+ *,
+ account_id: str,
+ type: Literal["publish_subscribe", "subscribe"],
+ # 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[TokenRotateResponse]:
+ """Generates a new token for the specified type.
+
+ The old token is immediately
+ invalidated. Token value is shown once in the response.
+
+ Args:
+ account_id: Cloudflare account identifier.
+
+ type: Which token type to rotate.
+
+ 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 relay_id:
+ raise ValueError(f"Expected a non-empty value for `relay_id` but received {relay_id!r}")
+ return self._post(
+ path_template(
+ "/accounts/{account_id}/moq/relays/{relay_id}/tokens/rotate", account_id=account_id, relay_id=relay_id
+ ),
+ body=maybe_transform({"type": type}, token_rotate_params.TokenRotateParams),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ post_parser=ResultWrapper[Optional[TokenRotateResponse]]._unwrapper,
+ ),
+ cast_to=cast(Type[Optional[TokenRotateResponse]], ResultWrapper[TokenRotateResponse]),
+ )
+
+
+class AsyncTokensResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncTokensResourceWithRawResponse:
+ """
+ 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 AsyncTokensResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncTokensResourceWithStreamingResponse:
+ """
+ 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 AsyncTokensResourceWithStreamingResponse(self)
+
+ async def rotate(
+ self,
+ relay_id: str,
+ *,
+ account_id: str,
+ type: Literal["publish_subscribe", "subscribe"],
+ # 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[TokenRotateResponse]:
+ """Generates a new token for the specified type.
+
+ The old token is immediately
+ invalidated. Token value is shown once in the response.
+
+ Args:
+ account_id: Cloudflare account identifier.
+
+ type: Which token type to rotate.
+
+ 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 relay_id:
+ raise ValueError(f"Expected a non-empty value for `relay_id` but received {relay_id!r}")
+ return await self._post(
+ path_template(
+ "/accounts/{account_id}/moq/relays/{relay_id}/tokens/rotate", account_id=account_id, relay_id=relay_id
+ ),
+ body=await async_maybe_transform({"type": type}, token_rotate_params.TokenRotateParams),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ post_parser=ResultWrapper[Optional[TokenRotateResponse]]._unwrapper,
+ ),
+ cast_to=cast(Type[Optional[TokenRotateResponse]], ResultWrapper[TokenRotateResponse]),
+ )
+
+
+class TokensResourceWithRawResponse:
+ def __init__(self, tokens: TokensResource) -> None:
+ self._tokens = tokens
+
+ self.rotate = to_raw_response_wrapper(
+ tokens.rotate,
+ )
+
+
+class AsyncTokensResourceWithRawResponse:
+ def __init__(self, tokens: AsyncTokensResource) -> None:
+ self._tokens = tokens
+
+ self.rotate = async_to_raw_response_wrapper(
+ tokens.rotate,
+ )
+
+
+class TokensResourceWithStreamingResponse:
+ def __init__(self, tokens: TokensResource) -> None:
+ self._tokens = tokens
+
+ self.rotate = to_streamed_response_wrapper(
+ tokens.rotate,
+ )
+
+
+class AsyncTokensResourceWithStreamingResponse:
+ def __init__(self, tokens: AsyncTokensResource) -> None:
+ self._tokens = tokens
+
+ self.rotate = async_to_streamed_response_wrapper(
+ tokens.rotate,
+ )
diff --git a/src/cloudflare/resources/zero_trust/organizations/organizations.py b/src/cloudflare/resources/zero_trust/organizations/organizations.py
index d66cc53d08b..a6bbf066792 100644
--- a/src/cloudflare/resources/zero_trust/organizations/organizations.py
+++ b/src/cloudflare/resources/zero_trust/organizations/organizations.py
@@ -76,8 +76,8 @@ def create(
is_ui_read_only: bool | Omit = omit,
login_design: LoginDesignParam | Omit = omit,
mfa_config: organization_create_params.MfaConfig | Omit = omit,
+ mfa_piv_key_requirements: organization_create_params.MfaPivKeyRequirements | Omit = omit,
mfa_required_for_all_apps: bool | Omit = omit,
- mfa_ssh_piv_key_requirements: organization_create_params.MfaSSHPivKeyRequirements | Omit = omit,
session_duration: str | Omit = omit,
ui_read_only_toggle_reason: str | Omit = omit,
user_seat_expiration_inactive_time: str | Omit = omit,
@@ -123,13 +123,13 @@ def create(
mfa_config: Configures multi-factor authentication (MFA) settings for an organization.
+ mfa_piv_key_requirements: Configures PIV key requirements for MFA using hardware security keys.
+
mfa_required_for_all_apps: Determines whether global MFA settings apply to applications by default. The
organization must have MFA enabled with at least one authentication method and a
session duration configured. Note: 'allowed_authenticators' cannot only contain
- 'ssh_piv_key' if the organization has any non-infrastructure applications
- because PIV keys are only compatible with infrastructure apps.
-
- mfa_ssh_piv_key_requirements: Configures SSH PIV key requirements for MFA using hardware security keys.
+ 'piv_key' if the organization has any non-infrastructure applications because
+ PIV keys are only compatible with infrastructure apps.
session_duration: The amount of time that tokens issued for applications will be valid. Must be in
the format `300ms` or `2h45m`. Valid time units are: ns, us (or µs), ms, s, m,
@@ -183,8 +183,8 @@ def create(
"is_ui_read_only": is_ui_read_only,
"login_design": login_design,
"mfa_config": mfa_config,
+ "mfa_piv_key_requirements": mfa_piv_key_requirements,
"mfa_required_for_all_apps": mfa_required_for_all_apps,
- "mfa_ssh_piv_key_requirements": mfa_ssh_piv_key_requirements,
"session_duration": session_duration,
"ui_read_only_toggle_reason": ui_read_only_toggle_reason,
"user_seat_expiration_inactive_time": user_seat_expiration_inactive_time,
@@ -216,8 +216,8 @@ def update(
is_ui_read_only: bool | Omit = omit,
login_design: LoginDesignParam | Omit = omit,
mfa_config: organization_update_params.MfaConfig | Omit = omit,
+ mfa_piv_key_requirements: organization_update_params.MfaPivKeyRequirements | Omit = omit,
mfa_required_for_all_apps: bool | Omit = omit,
- mfa_ssh_piv_key_requirements: organization_update_params.MfaSSHPivKeyRequirements | Omit = omit,
name: str | Omit = omit,
session_duration: str | Omit = omit,
ui_read_only_toggle_reason: str | Omit = omit,
@@ -262,13 +262,13 @@ def update(
mfa_config: Configures multi-factor authentication (MFA) settings for an organization.
+ mfa_piv_key_requirements: Configures PIV key requirements for MFA using hardware security keys.
+
mfa_required_for_all_apps: Determines whether global MFA settings apply to applications by default. The
organization must have MFA enabled with at least one authentication method and a
session duration configured. Note: 'allowed_authenticators' cannot only contain
- 'ssh_piv_key' if the organization has any non-infrastructure applications
- because PIV keys are only compatible with infrastructure apps.
-
- mfa_ssh_piv_key_requirements: Configures SSH PIV key requirements for MFA using hardware security keys.
+ 'piv_key' if the organization has any non-infrastructure applications because
+ PIV keys are only compatible with infrastructure apps.
name: The name of your Zero Trust organization.
@@ -324,8 +324,8 @@ def update(
"is_ui_read_only": is_ui_read_only,
"login_design": login_design,
"mfa_config": mfa_config,
+ "mfa_piv_key_requirements": mfa_piv_key_requirements,
"mfa_required_for_all_apps": mfa_required_for_all_apps,
- "mfa_ssh_piv_key_requirements": mfa_ssh_piv_key_requirements,
"name": name,
"session_duration": session_duration,
"ui_read_only_toggle_reason": ui_read_only_toggle_reason,
@@ -526,8 +526,8 @@ async def create(
is_ui_read_only: bool | Omit = omit,
login_design: LoginDesignParam | Omit = omit,
mfa_config: organization_create_params.MfaConfig | Omit = omit,
+ mfa_piv_key_requirements: organization_create_params.MfaPivKeyRequirements | Omit = omit,
mfa_required_for_all_apps: bool | Omit = omit,
- mfa_ssh_piv_key_requirements: organization_create_params.MfaSSHPivKeyRequirements | Omit = omit,
session_duration: str | Omit = omit,
ui_read_only_toggle_reason: str | Omit = omit,
user_seat_expiration_inactive_time: str | Omit = omit,
@@ -573,13 +573,13 @@ async def create(
mfa_config: Configures multi-factor authentication (MFA) settings for an organization.
+ mfa_piv_key_requirements: Configures PIV key requirements for MFA using hardware security keys.
+
mfa_required_for_all_apps: Determines whether global MFA settings apply to applications by default. The
organization must have MFA enabled with at least one authentication method and a
session duration configured. Note: 'allowed_authenticators' cannot only contain
- 'ssh_piv_key' if the organization has any non-infrastructure applications
- because PIV keys are only compatible with infrastructure apps.
-
- mfa_ssh_piv_key_requirements: Configures SSH PIV key requirements for MFA using hardware security keys.
+ 'piv_key' if the organization has any non-infrastructure applications because
+ PIV keys are only compatible with infrastructure apps.
session_duration: The amount of time that tokens issued for applications will be valid. Must be in
the format `300ms` or `2h45m`. Valid time units are: ns, us (or µs), ms, s, m,
@@ -633,8 +633,8 @@ async def create(
"is_ui_read_only": is_ui_read_only,
"login_design": login_design,
"mfa_config": mfa_config,
+ "mfa_piv_key_requirements": mfa_piv_key_requirements,
"mfa_required_for_all_apps": mfa_required_for_all_apps,
- "mfa_ssh_piv_key_requirements": mfa_ssh_piv_key_requirements,
"session_duration": session_duration,
"ui_read_only_toggle_reason": ui_read_only_toggle_reason,
"user_seat_expiration_inactive_time": user_seat_expiration_inactive_time,
@@ -666,8 +666,8 @@ async def update(
is_ui_read_only: bool | Omit = omit,
login_design: LoginDesignParam | Omit = omit,
mfa_config: organization_update_params.MfaConfig | Omit = omit,
+ mfa_piv_key_requirements: organization_update_params.MfaPivKeyRequirements | Omit = omit,
mfa_required_for_all_apps: bool | Omit = omit,
- mfa_ssh_piv_key_requirements: organization_update_params.MfaSSHPivKeyRequirements | Omit = omit,
name: str | Omit = omit,
session_duration: str | Omit = omit,
ui_read_only_toggle_reason: str | Omit = omit,
@@ -712,13 +712,13 @@ async def update(
mfa_config: Configures multi-factor authentication (MFA) settings for an organization.
+ mfa_piv_key_requirements: Configures PIV key requirements for MFA using hardware security keys.
+
mfa_required_for_all_apps: Determines whether global MFA settings apply to applications by default. The
organization must have MFA enabled with at least one authentication method and a
session duration configured. Note: 'allowed_authenticators' cannot only contain
- 'ssh_piv_key' if the organization has any non-infrastructure applications
- because PIV keys are only compatible with infrastructure apps.
-
- mfa_ssh_piv_key_requirements: Configures SSH PIV key requirements for MFA using hardware security keys.
+ 'piv_key' if the organization has any non-infrastructure applications because
+ PIV keys are only compatible with infrastructure apps.
name: The name of your Zero Trust organization.
@@ -774,8 +774,8 @@ async def update(
"is_ui_read_only": is_ui_read_only,
"login_design": login_design,
"mfa_config": mfa_config,
+ "mfa_piv_key_requirements": mfa_piv_key_requirements,
"mfa_required_for_all_apps": mfa_required_for_all_apps,
- "mfa_ssh_piv_key_requirements": mfa_ssh_piv_key_requirements,
"name": name,
"session_duration": session_duration,
"ui_read_only_toggle_reason": ui_read_only_toggle_reason,
diff --git a/src/cloudflare/types/email_auth/__init__.py b/src/cloudflare/types/email_auth/__init__.py
new file mode 100644
index 00000000000..e0d836b6f81
--- /dev/null
+++ b/src/cloudflare/types/email_auth/__init__.py
@@ -0,0 +1,7 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .dmarc_report_edit_params import DMARCReportEditParams as DMARCReportEditParams
+from .dmarc_report_get_response import DMARCReportGetResponse as DMARCReportGetResponse
+from .dmarc_report_edit_response import DMARCReportEditResponse as DMARCReportEditResponse
diff --git a/src/cloudflare/types/email_auth/dmarc_report_edit_params.py b/src/cloudflare/types/email_auth/dmarc_report_edit_params.py
new file mode 100644
index 00000000000..4d154790632
--- /dev/null
+++ b/src/cloudflare/types/email_auth/dmarc_report_edit_params.py
@@ -0,0 +1,19 @@
+# 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 Required, TypedDict
+
+__all__ = ["DMARCReportEditParams"]
+
+
+class DMARCReportEditParams(TypedDict, total=False):
+ zone_id: Required[str]
+ """Identifier."""
+
+ enabled: Optional[bool]
+ """Enable or disable DMARC reports for this zone"""
+
+ skip_wizard: Optional[bool]
+ """Skip the DMARC setup wizard"""
diff --git a/src/cloudflare/types/email_auth/dmarc_report_edit_response.py b/src/cloudflare/types/email_auth/dmarc_report_edit_response.py
new file mode 100644
index 00000000000..5e8b1598303
--- /dev/null
+++ b/src/cloudflare/types/email_auth/dmarc_report_edit_response.py
@@ -0,0 +1,251 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = [
+ "DMARCReportEditResponse",
+ "ApprovedSource",
+ "Records",
+ "RecordsBimiRecord",
+ "RecordsCnamedkimRecord",
+ "RecordsCnamedmarcRecord",
+ "RecordsCnamespfRecord",
+ "RecordsDKIMRecord",
+ "RecordsDMARCRecord",
+ "RecordsSPFRecord",
+]
+
+
+class ApprovedSource(BaseModel):
+ """A single approved sending source"""
+
+ created: Optional[datetime] = None
+ """Deprecated, use created_at"""
+
+ created_at: Optional[datetime] = None
+ """Creation timestamp"""
+
+ domain: Optional[str] = None
+ """The source domain"""
+
+ ips: Optional[List[str]] = None
+ """Resolved IP addresses from SPF"""
+
+ modified: Optional[datetime] = None
+ """Deprecated, use modified_at"""
+
+ modified_at: Optional[datetime] = None
+ """Last modification timestamp"""
+
+ name: Optional[str] = None
+ """Source name (typically same as domain)"""
+
+ slug: Optional[str] = None
+ """URL-friendly identifier"""
+
+ tag: Optional[str] = None
+ """Source UUID"""
+
+
+class RecordsBimiRecord(BaseModel):
+ """Summary of a single DNS record"""
+
+ id: Optional[str] = None
+ """DNS record ID"""
+
+ content: Optional[str] = None
+ """Record content"""
+
+ name: Optional[str] = None
+ """DNS record name"""
+
+ ttl: Optional[int] = None
+ """Time to live in seconds"""
+
+ type: Optional[str] = None
+ """Record type"""
+
+
+class RecordsCnamedkimRecord(BaseModel):
+ """Summary of a single DNS record"""
+
+ id: Optional[str] = None
+ """DNS record ID"""
+
+ content: Optional[str] = None
+ """Record content"""
+
+ name: Optional[str] = None
+ """DNS record name"""
+
+ ttl: Optional[int] = None
+ """Time to live in seconds"""
+
+ type: Optional[str] = None
+ """Record type"""
+
+
+class RecordsCnamedmarcRecord(BaseModel):
+ """Summary of a single DNS record"""
+
+ id: Optional[str] = None
+ """DNS record ID"""
+
+ content: Optional[str] = None
+ """Record content"""
+
+ name: Optional[str] = None
+ """DNS record name"""
+
+ ttl: Optional[int] = None
+ """Time to live in seconds"""
+
+ type: Optional[str] = None
+ """Record type"""
+
+
+class RecordsCnamespfRecord(BaseModel):
+ """Summary of a single DNS record"""
+
+ id: Optional[str] = None
+ """DNS record ID"""
+
+ content: Optional[str] = None
+ """Record content"""
+
+ name: Optional[str] = None
+ """DNS record name"""
+
+ ttl: Optional[int] = None
+ """Time to live in seconds"""
+
+ type: Optional[str] = None
+ """Record type"""
+
+
+class RecordsDKIMRecord(BaseModel):
+ """Summary of a single DNS record"""
+
+ id: Optional[str] = None
+ """DNS record ID"""
+
+ content: Optional[str] = None
+ """Record content"""
+
+ name: Optional[str] = None
+ """DNS record name"""
+
+ ttl: Optional[int] = None
+ """Time to live in seconds"""
+
+ type: Optional[str] = None
+ """Record type"""
+
+
+class RecordsDMARCRecord(BaseModel):
+ """Summary of a single DNS record"""
+
+ id: Optional[str] = None
+ """DNS record ID"""
+
+ content: Optional[str] = None
+ """Record content"""
+
+ name: Optional[str] = None
+ """DNS record name"""
+
+ ttl: Optional[int] = None
+ """Time to live in seconds"""
+
+ type: Optional[str] = None
+ """Record type"""
+
+
+class RecordsSPFRecord(BaseModel):
+ """Summary of a single DNS record"""
+
+ id: Optional[str] = None
+ """DNS record ID"""
+
+ content: Optional[str] = None
+ """Record content"""
+
+ name: Optional[str] = None
+ """DNS record name"""
+
+ ttl: Optional[int] = None
+ """Time to live in seconds"""
+
+ type: Optional[str] = None
+ """Record type"""
+
+
+class Records(BaseModel):
+ """Live DNS records for the zone, grouped by type"""
+
+ bimi_records: Optional[List[RecordsBimiRecord]] = None
+ """BIMI TXT records"""
+
+ cname_dkim_records: Optional[List[RecordsCnamedkimRecord]] = None
+ """CNAME records for DKIM"""
+
+ cname_dmarc_records: Optional[List[RecordsCnamedmarcRecord]] = None
+ """CNAME records at \\__dmarc (problematic)"""
+
+ cname_spf_records: Optional[List[RecordsCnamespfRecord]] = None
+ """CNAME records for SPF"""
+
+ dkim_records: Optional[List[RecordsDKIMRecord]] = None
+ """DKIM TXT records"""
+
+ dmarc_records: Optional[List[RecordsDMARCRecord]] = None
+ """DMARC TXT records"""
+
+ spf_records: Optional[List[RecordsSPFRecord]] = None
+ """SPF TXT records"""
+
+
+class DMARCReportEditResponse(BaseModel):
+ """Response for GET/PATCH /dmarc-reports"""
+
+ approved_sources: Optional[List[ApprovedSource]] = None
+ """List of approved sending sources (omitted when empty)"""
+
+ created: Optional[datetime] = None
+ """Deprecated, use created_at"""
+
+ created_at: Optional[datetime] = None
+ """Creation timestamp"""
+
+ enabled: Optional[bool] = None
+ """Whether DMARC reports are enabled"""
+
+ modified: Optional[datetime] = None
+ """Deprecated, use modified_at"""
+
+ modified_at: Optional[datetime] = None
+ """Last modification timestamp"""
+
+ records: Optional[Records] = None
+ """Live DNS records for the zone, grouped by type"""
+
+ rua_prefix: Optional[str] = None
+ """Prefix for DMARC RUA addresses (32-char hex string)"""
+
+ skip_wizard: Optional[bool] = None
+ """Whether to skip the setup wizard"""
+
+ status: Optional[
+ Literal["missing-dmarc-report", "multiple-dmarc-reports", "missing-dmarc-rua", "cname-on-dmarc-record"]
+ ] = None
+ """DMARC configuration status"""
+
+ tag: Optional[str] = None
+ """Use `zone_id` instead"""
+
+ zone_id: Optional[str] = None
+ """Zone identifier"""
diff --git a/src/cloudflare/types/email_auth/dmarc_report_get_response.py b/src/cloudflare/types/email_auth/dmarc_report_get_response.py
new file mode 100644
index 00000000000..c8457be9bbb
--- /dev/null
+++ b/src/cloudflare/types/email_auth/dmarc_report_get_response.py
@@ -0,0 +1,251 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = [
+ "DMARCReportGetResponse",
+ "ApprovedSource",
+ "Records",
+ "RecordsBimiRecord",
+ "RecordsCnamedkimRecord",
+ "RecordsCnamedmarcRecord",
+ "RecordsCnamespfRecord",
+ "RecordsDKIMRecord",
+ "RecordsDMARCRecord",
+ "RecordsSPFRecord",
+]
+
+
+class ApprovedSource(BaseModel):
+ """A single approved sending source"""
+
+ created: Optional[datetime] = None
+ """Deprecated, use created_at"""
+
+ created_at: Optional[datetime] = None
+ """Creation timestamp"""
+
+ domain: Optional[str] = None
+ """The source domain"""
+
+ ips: Optional[List[str]] = None
+ """Resolved IP addresses from SPF"""
+
+ modified: Optional[datetime] = None
+ """Deprecated, use modified_at"""
+
+ modified_at: Optional[datetime] = None
+ """Last modification timestamp"""
+
+ name: Optional[str] = None
+ """Source name (typically same as domain)"""
+
+ slug: Optional[str] = None
+ """URL-friendly identifier"""
+
+ tag: Optional[str] = None
+ """Source UUID"""
+
+
+class RecordsBimiRecord(BaseModel):
+ """Summary of a single DNS record"""
+
+ id: Optional[str] = None
+ """DNS record ID"""
+
+ content: Optional[str] = None
+ """Record content"""
+
+ name: Optional[str] = None
+ """DNS record name"""
+
+ ttl: Optional[int] = None
+ """Time to live in seconds"""
+
+ type: Optional[str] = None
+ """Record type"""
+
+
+class RecordsCnamedkimRecord(BaseModel):
+ """Summary of a single DNS record"""
+
+ id: Optional[str] = None
+ """DNS record ID"""
+
+ content: Optional[str] = None
+ """Record content"""
+
+ name: Optional[str] = None
+ """DNS record name"""
+
+ ttl: Optional[int] = None
+ """Time to live in seconds"""
+
+ type: Optional[str] = None
+ """Record type"""
+
+
+class RecordsCnamedmarcRecord(BaseModel):
+ """Summary of a single DNS record"""
+
+ id: Optional[str] = None
+ """DNS record ID"""
+
+ content: Optional[str] = None
+ """Record content"""
+
+ name: Optional[str] = None
+ """DNS record name"""
+
+ ttl: Optional[int] = None
+ """Time to live in seconds"""
+
+ type: Optional[str] = None
+ """Record type"""
+
+
+class RecordsCnamespfRecord(BaseModel):
+ """Summary of a single DNS record"""
+
+ id: Optional[str] = None
+ """DNS record ID"""
+
+ content: Optional[str] = None
+ """Record content"""
+
+ name: Optional[str] = None
+ """DNS record name"""
+
+ ttl: Optional[int] = None
+ """Time to live in seconds"""
+
+ type: Optional[str] = None
+ """Record type"""
+
+
+class RecordsDKIMRecord(BaseModel):
+ """Summary of a single DNS record"""
+
+ id: Optional[str] = None
+ """DNS record ID"""
+
+ content: Optional[str] = None
+ """Record content"""
+
+ name: Optional[str] = None
+ """DNS record name"""
+
+ ttl: Optional[int] = None
+ """Time to live in seconds"""
+
+ type: Optional[str] = None
+ """Record type"""
+
+
+class RecordsDMARCRecord(BaseModel):
+ """Summary of a single DNS record"""
+
+ id: Optional[str] = None
+ """DNS record ID"""
+
+ content: Optional[str] = None
+ """Record content"""
+
+ name: Optional[str] = None
+ """DNS record name"""
+
+ ttl: Optional[int] = None
+ """Time to live in seconds"""
+
+ type: Optional[str] = None
+ """Record type"""
+
+
+class RecordsSPFRecord(BaseModel):
+ """Summary of a single DNS record"""
+
+ id: Optional[str] = None
+ """DNS record ID"""
+
+ content: Optional[str] = None
+ """Record content"""
+
+ name: Optional[str] = None
+ """DNS record name"""
+
+ ttl: Optional[int] = None
+ """Time to live in seconds"""
+
+ type: Optional[str] = None
+ """Record type"""
+
+
+class Records(BaseModel):
+ """Live DNS records for the zone, grouped by type"""
+
+ bimi_records: Optional[List[RecordsBimiRecord]] = None
+ """BIMI TXT records"""
+
+ cname_dkim_records: Optional[List[RecordsCnamedkimRecord]] = None
+ """CNAME records for DKIM"""
+
+ cname_dmarc_records: Optional[List[RecordsCnamedmarcRecord]] = None
+ """CNAME records at \\__dmarc (problematic)"""
+
+ cname_spf_records: Optional[List[RecordsCnamespfRecord]] = None
+ """CNAME records for SPF"""
+
+ dkim_records: Optional[List[RecordsDKIMRecord]] = None
+ """DKIM TXT records"""
+
+ dmarc_records: Optional[List[RecordsDMARCRecord]] = None
+ """DMARC TXT records"""
+
+ spf_records: Optional[List[RecordsSPFRecord]] = None
+ """SPF TXT records"""
+
+
+class DMARCReportGetResponse(BaseModel):
+ """Response for GET/PATCH /dmarc-reports"""
+
+ approved_sources: Optional[List[ApprovedSource]] = None
+ """List of approved sending sources (omitted when empty)"""
+
+ created: Optional[datetime] = None
+ """Deprecated, use created_at"""
+
+ created_at: Optional[datetime] = None
+ """Creation timestamp"""
+
+ enabled: Optional[bool] = None
+ """Whether DMARC reports are enabled"""
+
+ modified: Optional[datetime] = None
+ """Deprecated, use modified_at"""
+
+ modified_at: Optional[datetime] = None
+ """Last modification timestamp"""
+
+ records: Optional[Records] = None
+ """Live DNS records for the zone, grouped by type"""
+
+ rua_prefix: Optional[str] = None
+ """Prefix for DMARC RUA addresses (32-char hex string)"""
+
+ skip_wizard: Optional[bool] = None
+ """Whether to skip the setup wizard"""
+
+ status: Optional[
+ Literal["missing-dmarc-report", "multiple-dmarc-reports", "missing-dmarc-rua", "cname-on-dmarc-record"]
+ ] = None
+ """DMARC configuration status"""
+
+ tag: Optional[str] = None
+ """Use `zone_id` instead"""
+
+ zone_id: Optional[str] = None
+ """Zone identifier"""
diff --git a/src/cloudflare/types/email_auth/spf/__init__.py b/src/cloudflare/types/email_auth/spf/__init__.py
new file mode 100644
index 00000000000..bbfb6b9d3ca
--- /dev/null
+++ b/src/cloudflare/types/email_auth/spf/__init__.py
@@ -0,0 +1,6 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .inspect_get_params import InspectGetParams as InspectGetParams
+from .inspect_get_response import InspectGetResponse as InspectGetResponse
diff --git a/src/cloudflare/types/email_auth/spf/inspect_get_params.py b/src/cloudflare/types/email_auth/spf/inspect_get_params.py
new file mode 100644
index 00000000000..594a4932b37
--- /dev/null
+++ b/src/cloudflare/types/email_auth/spf/inspect_get_params.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["InspectGetParams"]
+
+
+class InspectGetParams(TypedDict, total=False):
+ zone_id: Required[str]
+ """Identifier."""
+
+ id: Required[str]
+ """DNS record ID (rec_tag) to inspect"""
diff --git a/src/cloudflare/types/email_auth/spf/inspect_get_response.py b/src/cloudflare/types/email_auth/spf/inspect_get_response.py
new file mode 100644
index 00000000000..0d743a61602
--- /dev/null
+++ b/src/cloudflare/types/email_auth/spf/inspect_get_response.py
@@ -0,0 +1,66 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+
+from ...._models import BaseModel
+
+__all__ = ["InspectGetResponse", "Error"]
+
+
+class Error(BaseModel):
+ """An error encountered during SPF inspection"""
+
+ code: str
+ """Error code. Known values:
+
+ - `lookup_failed` — DNS TXT lookup failed
+ - `spf_not_found` — no SPF record found
+ - `invalid_spf` — record does not start with `v=spf1`
+ - `invalid_domain` — PSL validation failed
+ - `loop_detected` — include/redirect cycle detected
+ - `invalid_mechanism` — unrecognised or malformed mechanism
+ - `resource_limit_exceeded` — internal resource protection limits exceeded
+ (recursion depth or query budget)
+ - `max_lookups` — RFC 7208 10-lookup limit exceeded
+ """
+
+ domain: str
+ """Domain where the error occurred"""
+
+ message: str
+ """Human-readable error message"""
+
+ details: Optional[str] = None
+ """Additional error-specific details (optional).
+
+ - For `invalid_domain` errors: the invalid domain string
+ - For `invalid_mechanism` errors: the invalid mechanism text (e.g.,
+ "invalidmech123")
+ - For `loop_detected` errors: the domain that caused the loop
+ - For other error types: not present
+ """
+
+
+class InspectGetResponse(BaseModel):
+ """Recursive SPF inspection tree"""
+
+ components: List[object]
+ """Parsed SPF components (mechanisms)"""
+
+ domain: str
+ """Domain being inspected"""
+
+ record: str
+ """Raw SPF record content"""
+
+ total_lookups: int
+ """Total number of DNS lookups performed across all includes"""
+
+ errors: Optional[List[Error]] = None
+ """
+ All errors encountered during inspection, collected from the entire tree. This
+ includes errors from nested includes at any depth, providing a quick overview of
+ all issues without needing to traverse the nested structure. Each error includes
+ a `domain` field to identify where it occurred. Empty array if no errors
+ (omitted from JSON when empty).
+ """
diff --git a/src/cloudflare/types/email_routing/__init__.py b/src/cloudflare/types/email_routing/__init__.py
index 553bab1e508..9eaa27e5189 100644
--- a/src/cloudflare/types/email_routing/__init__.py
+++ b/src/cloudflare/types/email_routing/__init__.py
@@ -12,7 +12,6 @@
from .dns_get_params import DNSGetParams as DNSGetParams
from .dns_edit_params import DNSEditParams as DNSEditParams
from .dns_get_response import DNSGetResponse as DNSGetResponse
-from .rule_list_params import RuleListParams as RuleListParams
from .dns_create_params import DNSCreateParams as DNSCreateParams
from .email_routing_rule import EmailRoutingRule as EmailRoutingRule
from .rule_create_params import RuleCreateParams as RuleCreateParams
diff --git a/src/cloudflare/types/email_routing/rule_list_params.py b/src/cloudflare/types/email_routing/rule_list_params.py
deleted file mode 100644
index bb27b3d4885..00000000000
--- a/src/cloudflare/types/email_routing/rule_list_params.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from __future__ import annotations
-
-from typing_extensions import Literal, Required, TypedDict
-
-__all__ = ["RuleListParams"]
-
-
-class RuleListParams(TypedDict, total=False):
- zone_id: Required[str]
- """Identifier."""
-
- enabled: Literal[True, False]
- """Filter by enabled routing rules."""
-
- page: float
- """Page number of paginated results."""
-
- per_page: float
- """Maximum number of results per page."""
diff --git a/src/cloudflare/types/email_routing/settings.py b/src/cloudflare/types/email_routing/settings.py
index 9688cfbe2d1..900c3cfce4d 100644
--- a/src/cloudflare/types/email_routing/settings.py
+++ b/src/cloudflare/types/email_routing/settings.py
@@ -31,6 +31,12 @@ class Settings(BaseModel):
status: Optional[Literal["ready", "unconfigured", "misconfigured", "misconfigured/locked", "unlocked"]] = None
"""Show the state of your account, and the type or configuration error."""
+ support_subaddress: Optional[Literal[True, False]] = None
+ """
+ Whether subaddressing (plus-addressing) is honored when matching incoming mail
+ against routing rules.
+ """
+
tag: Optional[str] = None
"""Email Routing settings tag.
diff --git a/src/cloudflare/types/email_security/investigate/move_bulk_params.py b/src/cloudflare/types/email_security/investigate/move_bulk_params.py
index 35a0e5ac6d6..1942bb4ded6 100644
--- a/src/cloudflare/types/email_security/investigate/move_bulk_params.py
+++ b/src/cloudflare/types/email_security/investigate/move_bulk_params.py
@@ -17,6 +17,10 @@ class MoveBulkParams(TypedDict, total=False):
Literal["Inbox", "JunkEmail", "DeletedItems", "RecoverableItemsDeletions", "RecoverableItemsPurges"]
]
+ expected_disposition: Literal[
+ "MALICIOUS", "MALICIOUS-BEC", "SUSPICIOUS", "SPOOF", "SPAM", "BULK", "ENCRYPTED", "EXTERNAL", "UNKNOWN", "NONE"
+ ]
+
ids: SequenceNotStr[str]
"""List of message IDs to move"""
diff --git a/src/cloudflare/types/email_security/investigate/move_create_params.py b/src/cloudflare/types/email_security/investigate/move_create_params.py
index 0e389b2ae93..fc7c88c605d 100644
--- a/src/cloudflare/types/email_security/investigate/move_create_params.py
+++ b/src/cloudflare/types/email_security/investigate/move_create_params.py
@@ -14,3 +14,7 @@ class MoveCreateParams(TypedDict, total=False):
destination: Required[
Literal["Inbox", "JunkEmail", "DeletedItems", "RecoverableItemsDeletions", "RecoverableItemsPurges"]
]
+
+ expected_disposition: Literal[
+ "MALICIOUS", "MALICIOUS-BEC", "SUSPICIOUS", "SPOOF", "SPAM", "BULK", "ENCRYPTED", "EXTERNAL", "UNKNOWN", "NONE"
+ ]
diff --git a/src/cloudflare/types/magic_transit/gre_tunnel_bulk_update_response.py b/src/cloudflare/types/magic_transit/gre_tunnel_bulk_update_response.py
index ffacd0ffc74..924cb5b6907 100644
--- a/src/cloudflare/types/magic_transit/gre_tunnel_bulk_update_response.py
+++ b/src/cloudflare/types/magic_transit/gre_tunnel_bulk_update_response.py
@@ -23,12 +23,18 @@ class ModifiedGRETunnelBGP(BaseModel):
customer_asn: int
"""ASN used on the customer end of the BGP session"""
+ export_filter_id: Optional[str] = None
+ """ID of the BGP filter profile applied to routes advertised to the customer."""
+
extra_prefixes: Optional[List[str]] = None
"""
Prefixes in this list will be advertised to the customer device, in addition to
the routes in the Magic routing table.
"""
+ import_filter_id: Optional[str] = None
+ """ID of the BGP filter profile applied to routes received from the customer."""
+
md5_key: Optional[str] = None
"""MD5 key to use for session authentication.
diff --git a/src/cloudflare/types/magic_transit/gre_tunnel_create_params.py b/src/cloudflare/types/magic_transit/gre_tunnel_create_params.py
index 19e85148542..2c45cd93e3b 100644
--- a/src/cloudflare/types/magic_transit/gre_tunnel_create_params.py
+++ b/src/cloudflare/types/magic_transit/gre_tunnel_create_params.py
@@ -81,12 +81,18 @@ class BGP(TypedDict, total=False):
customer_asn: Required[int]
"""ASN used on the customer end of the BGP session"""
+ export_filter_id: str
+ """ID of the BGP filter profile applied to routes advertised to the customer."""
+
extra_prefixes: SequenceNotStr[str]
"""
Prefixes in this list will be advertised to the customer device, in addition to
the routes in the Magic routing table.
"""
+ import_filter_id: str
+ """ID of the BGP filter profile applied to routes received from the customer."""
+
md5_key: str
"""MD5 key to use for session authentication.
diff --git a/src/cloudflare/types/magic_transit/gre_tunnel_create_response.py b/src/cloudflare/types/magic_transit/gre_tunnel_create_response.py
index ee95b6fa8b2..3ae3d7d2282 100644
--- a/src/cloudflare/types/magic_transit/gre_tunnel_create_response.py
+++ b/src/cloudflare/types/magic_transit/gre_tunnel_create_response.py
@@ -22,12 +22,18 @@ class BGP(BaseModel):
customer_asn: int
"""ASN used on the customer end of the BGP session"""
+ export_filter_id: Optional[str] = None
+ """ID of the BGP filter profile applied to routes advertised to the customer."""
+
extra_prefixes: Optional[List[str]] = None
"""
Prefixes in this list will be advertised to the customer device, in addition to
the routes in the Magic routing table.
"""
+ import_filter_id: Optional[str] = None
+ """ID of the BGP filter profile applied to routes received from the customer."""
+
md5_key: Optional[str] = None
"""MD5 key to use for session authentication.
diff --git a/src/cloudflare/types/magic_transit/gre_tunnel_delete_response.py b/src/cloudflare/types/magic_transit/gre_tunnel_delete_response.py
index 2607e9e41bf..dc494813de7 100644
--- a/src/cloudflare/types/magic_transit/gre_tunnel_delete_response.py
+++ b/src/cloudflare/types/magic_transit/gre_tunnel_delete_response.py
@@ -23,12 +23,18 @@ class DeletedGRETunnelBGP(BaseModel):
customer_asn: int
"""ASN used on the customer end of the BGP session"""
+ export_filter_id: Optional[str] = None
+ """ID of the BGP filter profile applied to routes advertised to the customer."""
+
extra_prefixes: Optional[List[str]] = None
"""
Prefixes in this list will be advertised to the customer device, in addition to
the routes in the Magic routing table.
"""
+ import_filter_id: Optional[str] = None
+ """ID of the BGP filter profile applied to routes received from the customer."""
+
md5_key: Optional[str] = None
"""MD5 key to use for session authentication.
diff --git a/src/cloudflare/types/magic_transit/gre_tunnel_get_response.py b/src/cloudflare/types/magic_transit/gre_tunnel_get_response.py
index ef8eb5cacd8..32fd9527373 100644
--- a/src/cloudflare/types/magic_transit/gre_tunnel_get_response.py
+++ b/src/cloudflare/types/magic_transit/gre_tunnel_get_response.py
@@ -23,12 +23,18 @@ class GRETunnelBGP(BaseModel):
customer_asn: int
"""ASN used on the customer end of the BGP session"""
+ export_filter_id: Optional[str] = None
+ """ID of the BGP filter profile applied to routes advertised to the customer."""
+
extra_prefixes: Optional[List[str]] = None
"""
Prefixes in this list will be advertised to the customer device, in addition to
the routes in the Magic routing table.
"""
+ import_filter_id: Optional[str] = None
+ """ID of the BGP filter profile applied to routes received from the customer."""
+
md5_key: Optional[str] = None
"""MD5 key to use for session authentication.
diff --git a/src/cloudflare/types/magic_transit/gre_tunnel_list_response.py b/src/cloudflare/types/magic_transit/gre_tunnel_list_response.py
index e4d0cc9ec77..88c54249db2 100644
--- a/src/cloudflare/types/magic_transit/gre_tunnel_list_response.py
+++ b/src/cloudflare/types/magic_transit/gre_tunnel_list_response.py
@@ -23,12 +23,18 @@ class GRETunnelBGP(BaseModel):
customer_asn: int
"""ASN used on the customer end of the BGP session"""
+ export_filter_id: Optional[str] = None
+ """ID of the BGP filter profile applied to routes advertised to the customer."""
+
extra_prefixes: Optional[List[str]] = None
"""
Prefixes in this list will be advertised to the customer device, in addition to
the routes in the Magic routing table.
"""
+ import_filter_id: Optional[str] = None
+ """ID of the BGP filter profile applied to routes received from the customer."""
+
md5_key: Optional[str] = None
"""MD5 key to use for session authentication.
diff --git a/src/cloudflare/types/magic_transit/gre_tunnel_update_response.py b/src/cloudflare/types/magic_transit/gre_tunnel_update_response.py
index a1bba508388..28ca8a99198 100644
--- a/src/cloudflare/types/magic_transit/gre_tunnel_update_response.py
+++ b/src/cloudflare/types/magic_transit/gre_tunnel_update_response.py
@@ -23,12 +23,18 @@ class ModifiedGRETunnelBGP(BaseModel):
customer_asn: int
"""ASN used on the customer end of the BGP session"""
+ export_filter_id: Optional[str] = None
+ """ID of the BGP filter profile applied to routes advertised to the customer."""
+
extra_prefixes: Optional[List[str]] = None
"""
Prefixes in this list will be advertised to the customer device, in addition to
the routes in the Magic routing table.
"""
+ import_filter_id: Optional[str] = None
+ """ID of the BGP filter profile applied to routes received from the customer."""
+
md5_key: Optional[str] = None
"""MD5 key to use for session authentication.
diff --git a/src/cloudflare/types/magic_transit/ipsec_tunnel_bulk_update_response.py b/src/cloudflare/types/magic_transit/ipsec_tunnel_bulk_update_response.py
index 09d3a3e6bc4..2bd26657b61 100644
--- a/src/cloudflare/types/magic_transit/ipsec_tunnel_bulk_update_response.py
+++ b/src/cloudflare/types/magic_transit/ipsec_tunnel_bulk_update_response.py
@@ -25,12 +25,18 @@ class ModifiedIPSECTunnelBGP(BaseModel):
customer_asn: int
"""ASN used on the customer end of the BGP session"""
+ export_filter_id: Optional[str] = None
+ """ID of the BGP filter profile applied to routes advertised to the customer."""
+
extra_prefixes: Optional[List[str]] = None
"""
Prefixes in this list will be advertised to the customer device, in addition to
the routes in the Magic routing table.
"""
+ import_filter_id: Optional[str] = None
+ """ID of the BGP filter profile applied to routes received from the customer."""
+
md5_key: Optional[str] = None
"""MD5 key to use for session authentication.
diff --git a/src/cloudflare/types/magic_transit/ipsec_tunnel_create_params.py b/src/cloudflare/types/magic_transit/ipsec_tunnel_create_params.py
index cbfb544cfcf..42ad6c4cd85 100644
--- a/src/cloudflare/types/magic_transit/ipsec_tunnel_create_params.py
+++ b/src/cloudflare/types/magic_transit/ipsec_tunnel_create_params.py
@@ -83,12 +83,18 @@ class BGP(TypedDict, total=False):
customer_asn: Required[int]
"""ASN used on the customer end of the BGP session"""
+ export_filter_id: str
+ """ID of the BGP filter profile applied to routes advertised to the customer."""
+
extra_prefixes: SequenceNotStr[str]
"""
Prefixes in this list will be advertised to the customer device, in addition to
the routes in the Magic routing table.
"""
+ import_filter_id: str
+ """ID of the BGP filter profile applied to routes received from the customer."""
+
md5_key: str
"""MD5 key to use for session authentication.
diff --git a/src/cloudflare/types/magic_transit/ipsec_tunnel_create_response.py b/src/cloudflare/types/magic_transit/ipsec_tunnel_create_response.py
index 3dfa6eef0d3..3e415ceebb6 100644
--- a/src/cloudflare/types/magic_transit/ipsec_tunnel_create_response.py
+++ b/src/cloudflare/types/magic_transit/ipsec_tunnel_create_response.py
@@ -24,12 +24,18 @@ class BGP(BaseModel):
customer_asn: int
"""ASN used on the customer end of the BGP session"""
+ export_filter_id: Optional[str] = None
+ """ID of the BGP filter profile applied to routes advertised to the customer."""
+
extra_prefixes: Optional[List[str]] = None
"""
Prefixes in this list will be advertised to the customer device, in addition to
the routes in the Magic routing table.
"""
+ import_filter_id: Optional[str] = None
+ """ID of the BGP filter profile applied to routes received from the customer."""
+
md5_key: Optional[str] = None
"""MD5 key to use for session authentication.
diff --git a/src/cloudflare/types/magic_transit/ipsec_tunnel_delete_response.py b/src/cloudflare/types/magic_transit/ipsec_tunnel_delete_response.py
index 196c7dd3204..2b266a603ef 100644
--- a/src/cloudflare/types/magic_transit/ipsec_tunnel_delete_response.py
+++ b/src/cloudflare/types/magic_transit/ipsec_tunnel_delete_response.py
@@ -25,12 +25,18 @@ class DeletedIPSECTunnelBGP(BaseModel):
customer_asn: int
"""ASN used on the customer end of the BGP session"""
+ export_filter_id: Optional[str] = None
+ """ID of the BGP filter profile applied to routes advertised to the customer."""
+
extra_prefixes: Optional[List[str]] = None
"""
Prefixes in this list will be advertised to the customer device, in addition to
the routes in the Magic routing table.
"""
+ import_filter_id: Optional[str] = None
+ """ID of the BGP filter profile applied to routes received from the customer."""
+
md5_key: Optional[str] = None
"""MD5 key to use for session authentication.
diff --git a/src/cloudflare/types/magic_transit/ipsec_tunnel_get_response.py b/src/cloudflare/types/magic_transit/ipsec_tunnel_get_response.py
index f8bc64d3ba0..62e830f64ce 100644
--- a/src/cloudflare/types/magic_transit/ipsec_tunnel_get_response.py
+++ b/src/cloudflare/types/magic_transit/ipsec_tunnel_get_response.py
@@ -25,12 +25,18 @@ class IPSECTunnelBGP(BaseModel):
customer_asn: int
"""ASN used on the customer end of the BGP session"""
+ export_filter_id: Optional[str] = None
+ """ID of the BGP filter profile applied to routes advertised to the customer."""
+
extra_prefixes: Optional[List[str]] = None
"""
Prefixes in this list will be advertised to the customer device, in addition to
the routes in the Magic routing table.
"""
+ import_filter_id: Optional[str] = None
+ """ID of the BGP filter profile applied to routes received from the customer."""
+
md5_key: Optional[str] = None
"""MD5 key to use for session authentication.
diff --git a/src/cloudflare/types/magic_transit/ipsec_tunnel_list_response.py b/src/cloudflare/types/magic_transit/ipsec_tunnel_list_response.py
index 2b400df722d..5245477a780 100644
--- a/src/cloudflare/types/magic_transit/ipsec_tunnel_list_response.py
+++ b/src/cloudflare/types/magic_transit/ipsec_tunnel_list_response.py
@@ -25,12 +25,18 @@ class IPSECTunnelBGP(BaseModel):
customer_asn: int
"""ASN used on the customer end of the BGP session"""
+ export_filter_id: Optional[str] = None
+ """ID of the BGP filter profile applied to routes advertised to the customer."""
+
extra_prefixes: Optional[List[str]] = None
"""
Prefixes in this list will be advertised to the customer device, in addition to
the routes in the Magic routing table.
"""
+ import_filter_id: Optional[str] = None
+ """ID of the BGP filter profile applied to routes received from the customer."""
+
md5_key: Optional[str] = None
"""MD5 key to use for session authentication.
diff --git a/src/cloudflare/types/magic_transit/ipsec_tunnel_update_params.py b/src/cloudflare/types/magic_transit/ipsec_tunnel_update_params.py
index b8d6325f32e..c4d9e5b9bf4 100644
--- a/src/cloudflare/types/magic_transit/ipsec_tunnel_update_params.py
+++ b/src/cloudflare/types/magic_transit/ipsec_tunnel_update_params.py
@@ -83,12 +83,18 @@ class BGP(TypedDict, total=False):
customer_asn: Required[int]
"""ASN used on the customer end of the BGP session"""
+ export_filter_id: str
+ """ID of the BGP filter profile applied to routes advertised to the customer."""
+
extra_prefixes: SequenceNotStr[str]
"""
Prefixes in this list will be advertised to the customer device, in addition to
the routes in the Magic routing table.
"""
+ import_filter_id: str
+ """ID of the BGP filter profile applied to routes received from the customer."""
+
md5_key: str
"""MD5 key to use for session authentication.
diff --git a/src/cloudflare/types/magic_transit/ipsec_tunnel_update_response.py b/src/cloudflare/types/magic_transit/ipsec_tunnel_update_response.py
index 9e82141a51d..217447332b4 100644
--- a/src/cloudflare/types/magic_transit/ipsec_tunnel_update_response.py
+++ b/src/cloudflare/types/magic_transit/ipsec_tunnel_update_response.py
@@ -25,12 +25,18 @@ class ModifiedIPSECTunnelBGP(BaseModel):
customer_asn: int
"""ASN used on the customer end of the BGP session"""
+ export_filter_id: Optional[str] = None
+ """ID of the BGP filter profile applied to routes advertised to the customer."""
+
extra_prefixes: Optional[List[str]] = None
"""
Prefixes in this list will be advertised to the customer device, in addition to
the routes in the Magic routing table.
"""
+ import_filter_id: Optional[str] = None
+ """ID of the BGP filter profile applied to routes received from the customer."""
+
md5_key: Optional[str] = None
"""MD5 key to use for session authentication.
diff --git a/src/cloudflare/types/moq/__init__.py b/src/cloudflare/types/moq/__init__.py
new file mode 100644
index 00000000000..6c12a15de24
--- /dev/null
+++ b/src/cloudflare/types/moq/__init__.py
@@ -0,0 +1,11 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .relay_list_params import RelayListParams as RelayListParams
+from .relay_get_response import RelayGetResponse as RelayGetResponse
+from .relay_create_params import RelayCreateParams as RelayCreateParams
+from .relay_list_response import RelayListResponse as RelayListResponse
+from .relay_update_params import RelayUpdateParams as RelayUpdateParams
+from .relay_create_response import RelayCreateResponse as RelayCreateResponse
+from .relay_update_response import RelayUpdateResponse as RelayUpdateResponse
diff --git a/src/cloudflare/types/moq/relay_create_params.py b/src/cloudflare/types/moq/relay_create_params.py
new file mode 100644
index 00000000000..104c3d032c1
--- /dev/null
+++ b/src/cloudflare/types/moq/relay_create_params.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["RelayCreateParams"]
+
+
+class RelayCreateParams(TypedDict, total=False):
+ account_id: Required[str]
+ """Cloudflare account identifier."""
+
+ name: Required[str]
+ """Human-readable name for the relay."""
diff --git a/src/cloudflare/types/moq/relay_create_response.py b/src/cloudflare/types/moq/relay_create_response.py
new file mode 100644
index 00000000000..1cf2516282e
--- /dev/null
+++ b/src/cloudflare/types/moq/relay_create_response.py
@@ -0,0 +1,69 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from datetime import datetime
+
+from ..._models import BaseModel
+
+__all__ = [
+ "RelayCreateResponse",
+ "Config",
+ "ConfigLingeringSubscribe",
+ "ConfigOriginFallback",
+ "ConfigOriginFallbackOrigin",
+]
+
+
+class ConfigLingeringSubscribe(BaseModel):
+ enabled: Optional[bool] = None
+
+ max_timeout_ms: Optional[int] = None
+ """Relay-level ceiling on lingering subscribe timeout (ms). Default 30000."""
+
+
+class ConfigOriginFallbackOrigin(BaseModel):
+ """A single upstream origin relay."""
+
+ url: Optional[str] = None
+ """Upstream origin relay URL."""
+
+
+class ConfigOriginFallback(BaseModel):
+ enabled: Optional[bool] = None
+
+ origins: Optional[List[ConfigOriginFallbackOrigin]] = None
+ """Ordered list of upstream origin relays.
+
+ Each entry is an object (not a bare string) so per-origin configuration can be
+ added in the future without another breaking change.
+ """
+
+
+class Config(BaseModel):
+ """origin_fallback and lingering_subscribe are mutually exclusive."""
+
+ lingering_subscribe: Optional[ConfigLingeringSubscribe] = None
+
+ origin_fallback: Optional[ConfigOriginFallback] = None
+
+
+class RelayCreateResponse(BaseModel):
+ """Relay with auto-generated tokens (shown once)."""
+
+ config: Config
+ """origin_fallback and lingering_subscribe are mutually exclusive."""
+
+ created: datetime
+
+ modified: datetime
+
+ name: str
+
+ token_publish_subscribe: str
+ """Full access token (publish + subscribe). Treat as sensitive."""
+
+ token_subscribe: str
+ """Subscribe-only token. Treat as sensitive."""
+
+ uid: str
+ """Server-generated unique identifier (32 hex chars)."""
diff --git a/src/cloudflare/types/moq/relay_get_response.py b/src/cloudflare/types/moq/relay_get_response.py
new file mode 100644
index 00000000000..b8d008ebd7e
--- /dev/null
+++ b/src/cloudflare/types/moq/relay_get_response.py
@@ -0,0 +1,66 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = [
+ "RelayGetResponse",
+ "Config",
+ "ConfigLingeringSubscribe",
+ "ConfigOriginFallback",
+ "ConfigOriginFallbackOrigin",
+]
+
+
+class ConfigLingeringSubscribe(BaseModel):
+ enabled: Optional[bool] = None
+
+ max_timeout_ms: Optional[int] = None
+ """Relay-level ceiling on lingering subscribe timeout (ms). Default 30000."""
+
+
+class ConfigOriginFallbackOrigin(BaseModel):
+ """A single upstream origin relay."""
+
+ url: Optional[str] = None
+ """Upstream origin relay URL."""
+
+
+class ConfigOriginFallback(BaseModel):
+ enabled: Optional[bool] = None
+
+ origins: Optional[List[ConfigOriginFallbackOrigin]] = None
+ """Ordered list of upstream origin relays.
+
+ Each entry is an object (not a bare string) so per-origin configuration can be
+ added in the future without another breaking change.
+ """
+
+
+class Config(BaseModel):
+ """origin_fallback and lingering_subscribe are mutually exclusive."""
+
+ lingering_subscribe: Optional[ConfigLingeringSubscribe] = None
+
+ origin_fallback: Optional[ConfigOriginFallback] = None
+
+
+class RelayGetResponse(BaseModel):
+ """Full relay details (no tokens)."""
+
+ config: Config
+ """origin_fallback and lingering_subscribe are mutually exclusive."""
+
+ created: datetime
+
+ modified: datetime
+
+ name: str
+
+ uid: str
+
+ status: Optional[Literal["connected"]] = None
+ """\"connected" when active, omitted otherwise."""
diff --git a/src/cloudflare/types/moq/relay_list_params.py b/src/cloudflare/types/moq/relay_list_params.py
new file mode 100644
index 00000000000..6abd6a25248
--- /dev/null
+++ b/src/cloudflare/types/moq/relay_list_params.py
@@ -0,0 +1,41 @@
+# 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 Required, Annotated, TypedDict
+
+from ..._utils import PropertyInfo
+
+__all__ = ["RelayListParams"]
+
+
+class RelayListParams(TypedDict, total=False):
+ account_id: Required[str]
+ """Cloudflare account identifier."""
+
+ asc: bool
+ """Sort order by `created`.
+
+ When true, results are returned oldest-first (ascending); otherwise newest-first
+ (descending, the default).
+ """
+
+ created_after: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")]
+ """Cursor for pagination.
+
+ Returns relays created strictly after this RFC 3339 timestamp (typically the
+ `created` value of the last item on the current page, to fetch the next page).
+ """
+
+ created_before: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")]
+ """Cursor for pagination.
+
+ Returns relays created strictly before this RFC 3339 timestamp (typically the
+ `created` value of the first item on the current page, to fetch the previous
+ page).
+ """
+
+ per_page: int
+ """Maximum number of relays to return per page."""
diff --git a/src/cloudflare/types/moq/relay_list_response.py b/src/cloudflare/types/moq/relay_list_response.py
new file mode 100644
index 00000000000..00576f6b42a
--- /dev/null
+++ b/src/cloudflare/types/moq/relay_list_response.py
@@ -0,0 +1,19 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from datetime import datetime
+
+from ..._models import BaseModel
+
+__all__ = ["RelayListResponse"]
+
+
+class RelayListResponse(BaseModel):
+ """Abbreviated relay for list responses."""
+
+ created: datetime
+
+ modified: datetime
+
+ name: str
+
+ uid: str
diff --git a/src/cloudflare/types/moq/relay_update_params.py b/src/cloudflare/types/moq/relay_update_params.py
new file mode 100644
index 00000000000..1a00e6e8cf2
--- /dev/null
+++ b/src/cloudflare/types/moq/relay_update_params.py
@@ -0,0 +1,57 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Iterable
+from typing_extensions import Required, TypedDict
+
+__all__ = [
+ "RelayUpdateParams",
+ "Config",
+ "ConfigLingeringSubscribe",
+ "ConfigOriginFallback",
+ "ConfigOriginFallbackOrigin",
+]
+
+
+class RelayUpdateParams(TypedDict, total=False):
+ account_id: Required[str]
+ """Cloudflare account identifier."""
+
+ config: Config
+ """origin_fallback and lingering_subscribe are mutually exclusive."""
+
+ name: str
+
+
+class ConfigLingeringSubscribe(TypedDict, total=False):
+ enabled: bool
+
+ max_timeout_ms: int
+ """Relay-level ceiling on lingering subscribe timeout (ms). Default 30000."""
+
+
+class ConfigOriginFallbackOrigin(TypedDict, total=False):
+ """A single upstream origin relay."""
+
+ url: str
+ """Upstream origin relay URL."""
+
+
+class ConfigOriginFallback(TypedDict, total=False):
+ enabled: bool
+
+ origins: Iterable[ConfigOriginFallbackOrigin]
+ """Ordered list of upstream origin relays.
+
+ Each entry is an object (not a bare string) so per-origin configuration can be
+ added in the future without another breaking change.
+ """
+
+
+class Config(TypedDict, total=False):
+ """origin_fallback and lingering_subscribe are mutually exclusive."""
+
+ lingering_subscribe: ConfigLingeringSubscribe
+
+ origin_fallback: ConfigOriginFallback
diff --git a/src/cloudflare/types/moq/relay_update_response.py b/src/cloudflare/types/moq/relay_update_response.py
new file mode 100644
index 00000000000..10e35818945
--- /dev/null
+++ b/src/cloudflare/types/moq/relay_update_response.py
@@ -0,0 +1,66 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = [
+ "RelayUpdateResponse",
+ "Config",
+ "ConfigLingeringSubscribe",
+ "ConfigOriginFallback",
+ "ConfigOriginFallbackOrigin",
+]
+
+
+class ConfigLingeringSubscribe(BaseModel):
+ enabled: Optional[bool] = None
+
+ max_timeout_ms: Optional[int] = None
+ """Relay-level ceiling on lingering subscribe timeout (ms). Default 30000."""
+
+
+class ConfigOriginFallbackOrigin(BaseModel):
+ """A single upstream origin relay."""
+
+ url: Optional[str] = None
+ """Upstream origin relay URL."""
+
+
+class ConfigOriginFallback(BaseModel):
+ enabled: Optional[bool] = None
+
+ origins: Optional[List[ConfigOriginFallbackOrigin]] = None
+ """Ordered list of upstream origin relays.
+
+ Each entry is an object (not a bare string) so per-origin configuration can be
+ added in the future without another breaking change.
+ """
+
+
+class Config(BaseModel):
+ """origin_fallback and lingering_subscribe are mutually exclusive."""
+
+ lingering_subscribe: Optional[ConfigLingeringSubscribe] = None
+
+ origin_fallback: Optional[ConfigOriginFallback] = None
+
+
+class RelayUpdateResponse(BaseModel):
+ """Full relay details (no tokens)."""
+
+ config: Config
+ """origin_fallback and lingering_subscribe are mutually exclusive."""
+
+ created: datetime
+
+ modified: datetime
+
+ name: str
+
+ uid: str
+
+ status: Optional[Literal["connected"]] = None
+ """\"connected" when active, omitted otherwise."""
diff --git a/src/cloudflare/types/moq/relays/__init__.py b/src/cloudflare/types/moq/relays/__init__.py
new file mode 100644
index 00000000000..e1548b499d4
--- /dev/null
+++ b/src/cloudflare/types/moq/relays/__init__.py
@@ -0,0 +1,6 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .token_rotate_params import TokenRotateParams as TokenRotateParams
+from .token_rotate_response import TokenRotateResponse as TokenRotateResponse
diff --git a/src/cloudflare/types/moq/relays/token_rotate_params.py b/src/cloudflare/types/moq/relays/token_rotate_params.py
new file mode 100644
index 00000000000..07611c3cffc
--- /dev/null
+++ b/src/cloudflare/types/moq/relays/token_rotate_params.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["TokenRotateParams"]
+
+
+class TokenRotateParams(TypedDict, total=False):
+ account_id: Required[str]
+ """Cloudflare account identifier."""
+
+ type: Required[Literal["publish_subscribe", "subscribe"]]
+ """Which token type to rotate."""
diff --git a/src/cloudflare/types/moq/relays/token_rotate_response.py b/src/cloudflare/types/moq/relays/token_rotate_response.py
new file mode 100644
index 00000000000..5f71a3081dc
--- /dev/null
+++ b/src/cloudflare/types/moq/relays/token_rotate_response.py
@@ -0,0 +1,14 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ...._models import BaseModel
+
+__all__ = ["TokenRotateResponse"]
+
+
+class TokenRotateResponse(BaseModel):
+ token: str
+ """New token value (shown once). Treat as sensitive."""
+
+ type: Literal["publish_subscribe", "subscribe"]
diff --git a/src/cloudflare/types/rulesets/rule_create_params.py b/src/cloudflare/types/rulesets/rule_create_params.py
index 6a8c82757e1..9c5b7d9ea6b 100644
--- a/src/cloudflare/types/rulesets/rule_create_params.py
+++ b/src/cloudflare/types/rulesets/rule_create_params.py
@@ -220,6 +220,9 @@
"SetCacheSettingsRuleActionParametersEdgeTTLStatusCodeTTLStatusCodeRange",
"SetCacheSettingsRuleActionParametersServeStale",
"SetCacheSettingsRuleActionParametersSharedDictionary",
+ "SetCacheSettingsRuleActionParametersVary",
+ "SetCacheSettingsRuleActionParametersVaryDefault",
+ "SetCacheSettingsRuleActionParametersVaryHeaders",
"SetCacheSettingsRuleExposedCredentialCheck",
"SetCacheSettingsRulePosition",
"SetCacheSettingsRulePositionBeforePosition",
@@ -3425,6 +3428,64 @@ class SetCacheSettingsRuleActionParametersSharedDictionary(TypedDict, total=Fals
"""
+class SetCacheSettingsRuleActionParametersVaryDefault(TypedDict, total=False):
+ """
+ Controls how a single request header (or the default for all headers) contributes to the cache key.
+ """
+
+ action: Required[Literal["bypass", "passthrough", "normalize"]]
+ """How the header value is treated when building the cache key."""
+
+ languages: SequenceNotStr[str]
+ """The set of languages to normalize against.
+
+ Only valid for the `accept-language` header.
+ """
+
+ media_types: SequenceNotStr[str]
+ """The set of media types to normalize against.
+
+ Only valid for the `accept` header.
+ """
+
+
+class SetCacheSettingsRuleActionParametersVaryHeaders(TypedDict, total=False):
+ """
+ Controls how a single request header (or the default for all headers) contributes to the cache key.
+ """
+
+ action: Required[Literal["bypass", "passthrough", "normalize"]]
+ """How the header value is treated when building the cache key."""
+
+ languages: SequenceNotStr[str]
+ """The set of languages to normalize against.
+
+ Only valid for the `accept-language` header.
+ """
+
+ media_types: SequenceNotStr[str]
+ """The set of media types to normalize against.
+
+ Only valid for the `accept` header.
+ """
+
+
+class SetCacheSettingsRuleActionParametersVary(TypedDict, total=False):
+ """Controls how cached responses vary based on request headers.
+
+ At least one of `default` or `headers` must be set, and `default` is required when `headers` is set.
+ """
+
+ default: SetCacheSettingsRuleActionParametersVaryDefault
+ """
+ Controls how a single request header (or the default for all headers)
+ contributes to the cache key.
+ """
+
+ headers: Dict[str, SetCacheSettingsRuleActionParametersVaryHeaders]
+ """A mapping of lowercase request header names to their vary configuration."""
+
+
class SetCacheSettingsRuleActionParameters(TypedDict, total=False):
"""The parameters configuring the rule's action."""
@@ -3500,6 +3561,13 @@ class SetCacheSettingsRuleActionParameters(TypedDict, total=False):
strip_set_cookie: bool
"""Whether to strip Set-Cookie headers from the origin response before caching."""
+ vary: SetCacheSettingsRuleActionParametersVary
+ """Controls how cached responses vary based on request headers.
+
+ At least one of `default` or `headers` must be set, and `default` is required
+ when `headers` is set.
+ """
+
class SetCacheSettingsRuleExposedCredentialCheck(TypedDict, total=False):
"""Configuration for exposed credential checking."""
diff --git a/src/cloudflare/types/rulesets/rule_edit_params.py b/src/cloudflare/types/rulesets/rule_edit_params.py
index c914421bbdf..272ed9c3a2f 100644
--- a/src/cloudflare/types/rulesets/rule_edit_params.py
+++ b/src/cloudflare/types/rulesets/rule_edit_params.py
@@ -220,6 +220,9 @@
"SetCacheSettingsRuleActionParametersEdgeTTLStatusCodeTTLStatusCodeRange",
"SetCacheSettingsRuleActionParametersServeStale",
"SetCacheSettingsRuleActionParametersSharedDictionary",
+ "SetCacheSettingsRuleActionParametersVary",
+ "SetCacheSettingsRuleActionParametersVaryDefault",
+ "SetCacheSettingsRuleActionParametersVaryHeaders",
"SetCacheSettingsRuleExposedCredentialCheck",
"SetCacheSettingsRulePosition",
"SetCacheSettingsRulePositionBeforePosition",
@@ -3476,6 +3479,64 @@ class SetCacheSettingsRuleActionParametersSharedDictionary(TypedDict, total=Fals
"""
+class SetCacheSettingsRuleActionParametersVaryDefault(TypedDict, total=False):
+ """
+ Controls how a single request header (or the default for all headers) contributes to the cache key.
+ """
+
+ action: Required[Literal["bypass", "passthrough", "normalize"]]
+ """How the header value is treated when building the cache key."""
+
+ languages: SequenceNotStr[str]
+ """The set of languages to normalize against.
+
+ Only valid for the `accept-language` header.
+ """
+
+ media_types: SequenceNotStr[str]
+ """The set of media types to normalize against.
+
+ Only valid for the `accept` header.
+ """
+
+
+class SetCacheSettingsRuleActionParametersVaryHeaders(TypedDict, total=False):
+ """
+ Controls how a single request header (or the default for all headers) contributes to the cache key.
+ """
+
+ action: Required[Literal["bypass", "passthrough", "normalize"]]
+ """How the header value is treated when building the cache key."""
+
+ languages: SequenceNotStr[str]
+ """The set of languages to normalize against.
+
+ Only valid for the `accept-language` header.
+ """
+
+ media_types: SequenceNotStr[str]
+ """The set of media types to normalize against.
+
+ Only valid for the `accept` header.
+ """
+
+
+class SetCacheSettingsRuleActionParametersVary(TypedDict, total=False):
+ """Controls how cached responses vary based on request headers.
+
+ At least one of `default` or `headers` must be set, and `default` is required when `headers` is set.
+ """
+
+ default: SetCacheSettingsRuleActionParametersVaryDefault
+ """
+ Controls how a single request header (or the default for all headers)
+ contributes to the cache key.
+ """
+
+ headers: Dict[str, SetCacheSettingsRuleActionParametersVaryHeaders]
+ """A mapping of lowercase request header names to their vary configuration."""
+
+
class SetCacheSettingsRuleActionParameters(TypedDict, total=False):
"""The parameters configuring the rule's action."""
@@ -3551,6 +3612,13 @@ class SetCacheSettingsRuleActionParameters(TypedDict, total=False):
strip_set_cookie: bool
"""Whether to strip Set-Cookie headers from the origin response before caching."""
+ vary: SetCacheSettingsRuleActionParametersVary
+ """Controls how cached responses vary based on request headers.
+
+ At least one of `default` or `headers` must be set, and `default` is required
+ when `headers` is set.
+ """
+
class SetCacheSettingsRuleExposedCredentialCheck(TypedDict, total=False):
"""Configuration for exposed credential checking."""
diff --git a/src/cloudflare/types/rulesets/set_cache_settings_rule.py b/src/cloudflare/types/rulesets/set_cache_settings_rule.py
index 8b1f40e5af7..c5c8c06032f 100644
--- a/src/cloudflare/types/rulesets/set_cache_settings_rule.py
+++ b/src/cloudflare/types/rulesets/set_cache_settings_rule.py
@@ -28,6 +28,9 @@
"ActionParametersEdgeTTLStatusCodeTTLStatusCodeRange",
"ActionParametersServeStale",
"ActionParametersSharedDictionary",
+ "ActionParametersVary",
+ "ActionParametersVaryDefault",
+ "ActionParametersVaryHeaders",
"ExposedCredentialCheck",
"Ratelimit",
]
@@ -253,6 +256,64 @@ class ActionParametersSharedDictionary(BaseModel):
"""
+class ActionParametersVaryDefault(BaseModel):
+ """
+ Controls how a single request header (or the default for all headers) contributes to the cache key.
+ """
+
+ action: Literal["bypass", "passthrough", "normalize"]
+ """How the header value is treated when building the cache key."""
+
+ languages: Optional[List[str]] = None
+ """The set of languages to normalize against.
+
+ Only valid for the `accept-language` header.
+ """
+
+ media_types: Optional[List[str]] = None
+ """The set of media types to normalize against.
+
+ Only valid for the `accept` header.
+ """
+
+
+class ActionParametersVaryHeaders(BaseModel):
+ """
+ Controls how a single request header (or the default for all headers) contributes to the cache key.
+ """
+
+ action: Literal["bypass", "passthrough", "normalize"]
+ """How the header value is treated when building the cache key."""
+
+ languages: Optional[List[str]] = None
+ """The set of languages to normalize against.
+
+ Only valid for the `accept-language` header.
+ """
+
+ media_types: Optional[List[str]] = None
+ """The set of media types to normalize against.
+
+ Only valid for the `accept` header.
+ """
+
+
+class ActionParametersVary(BaseModel):
+ """Controls how cached responses vary based on request headers.
+
+ At least one of `default` or `headers` must be set, and `default` is required when `headers` is set.
+ """
+
+ default: Optional[ActionParametersVaryDefault] = None
+ """
+ Controls how a single request header (or the default for all headers)
+ contributes to the cache key.
+ """
+
+ headers: Optional[Dict[str, ActionParametersVaryHeaders]] = None
+ """A mapping of lowercase request header names to their vary configuration."""
+
+
class ActionParameters(BaseModel):
"""The parameters configuring the rule's action."""
@@ -328,6 +389,13 @@ class ActionParameters(BaseModel):
strip_set_cookie: Optional[bool] = None
"""Whether to strip Set-Cookie headers from the origin response before caching."""
+ vary: Optional[ActionParametersVary] = None
+ """Controls how cached responses vary based on request headers.
+
+ At least one of `default` or `headers` must be set, and `default` is required
+ when `headers` is set.
+ """
+
class ExposedCredentialCheck(BaseModel):
"""Configuration for exposed credential checking."""
diff --git a/src/cloudflare/types/rulesets/set_cache_settings_rule_param.py b/src/cloudflare/types/rulesets/set_cache_settings_rule_param.py
index b223d45297d..3ed01b1a11e 100644
--- a/src/cloudflare/types/rulesets/set_cache_settings_rule_param.py
+++ b/src/cloudflare/types/rulesets/set_cache_settings_rule_param.py
@@ -27,6 +27,9 @@
"ActionParametersEdgeTTLStatusCodeTTLStatusCodeRange",
"ActionParametersServeStale",
"ActionParametersSharedDictionary",
+ "ActionParametersVary",
+ "ActionParametersVaryDefault",
+ "ActionParametersVaryHeaders",
"ExposedCredentialCheck",
"Ratelimit",
]
@@ -260,6 +263,64 @@ class ActionParametersSharedDictionary(TypedDict, total=False):
"""
+class ActionParametersVaryDefault(TypedDict, total=False):
+ """
+ Controls how a single request header (or the default for all headers) contributes to the cache key.
+ """
+
+ action: Required[Literal["bypass", "passthrough", "normalize"]]
+ """How the header value is treated when building the cache key."""
+
+ languages: SequenceNotStr[str]
+ """The set of languages to normalize against.
+
+ Only valid for the `accept-language` header.
+ """
+
+ media_types: SequenceNotStr[str]
+ """The set of media types to normalize against.
+
+ Only valid for the `accept` header.
+ """
+
+
+class ActionParametersVaryHeaders(TypedDict, total=False):
+ """
+ Controls how a single request header (or the default for all headers) contributes to the cache key.
+ """
+
+ action: Required[Literal["bypass", "passthrough", "normalize"]]
+ """How the header value is treated when building the cache key."""
+
+ languages: SequenceNotStr[str]
+ """The set of languages to normalize against.
+
+ Only valid for the `accept-language` header.
+ """
+
+ media_types: SequenceNotStr[str]
+ """The set of media types to normalize against.
+
+ Only valid for the `accept` header.
+ """
+
+
+class ActionParametersVary(TypedDict, total=False):
+ """Controls how cached responses vary based on request headers.
+
+ At least one of `default` or `headers` must be set, and `default` is required when `headers` is set.
+ """
+
+ default: ActionParametersVaryDefault
+ """
+ Controls how a single request header (or the default for all headers)
+ contributes to the cache key.
+ """
+
+ headers: Dict[str, ActionParametersVaryHeaders]
+ """A mapping of lowercase request header names to their vary configuration."""
+
+
class ActionParameters(TypedDict, total=False):
"""The parameters configuring the rule's action."""
@@ -335,6 +396,13 @@ class ActionParameters(TypedDict, total=False):
strip_set_cookie: bool
"""Whether to strip Set-Cookie headers from the origin response before caching."""
+ vary: ActionParametersVary
+ """Controls how cached responses vary based on request headers.
+
+ At least one of `default` or `headers` must be set, and `default` is required
+ when `headers` is set.
+ """
+
class ExposedCredentialCheck(TypedDict, total=False):
"""Configuration for exposed credential checking."""
diff --git a/src/cloudflare/types/zero_trust/access/application_create_params.py b/src/cloudflare/types/zero_trust/access/application_create_params.py
index bc66ae1b3ee..a26b435aeff 100644
--- a/src/cloudflare/types/zero_trust/access/application_create_params.py
+++ b/src/cloudflare/types/zero_trust/access/application_create_params.py
@@ -3039,10 +3039,10 @@ class InfrastructureApplicationPolicyMfaConfig(TypedDict, total=False):
Configures multi-factor authentication (MFA) settings for infrastructure applications.
"""
- allowed_authenticators: List[Literal["ssh_piv_key"]]
+ allowed_authenticators: List[Literal["piv_key"]]
"""Lists the MFA methods that users can authenticate with.
- For infrastructure applications, only `ssh_piv_key` is supported.
+ For infrastructure applications, only `piv_key` is supported.
"""
mfa_disabled: bool
diff --git a/src/cloudflare/types/zero_trust/access/application_create_response.py b/src/cloudflare/types/zero_trust/access/application_create_response.py
index cc2f8dd4e3d..dd44a3f27ae 100644
--- a/src/cloudflare/types/zero_trust/access/application_create_response.py
+++ b/src/cloudflare/types/zero_trust/access/application_create_response.py
@@ -3068,10 +3068,10 @@ class InfrastructureApplicationPolicyMfaConfig(BaseModel):
Configures multi-factor authentication (MFA) settings for infrastructure applications.
"""
- allowed_authenticators: Optional[List[Literal["ssh_piv_key"]]] = None
+ allowed_authenticators: Optional[List[Literal["piv_key"]]] = None
"""Lists the MFA methods that users can authenticate with.
- For infrastructure applications, only `ssh_piv_key` is supported.
+ For infrastructure applications, only `piv_key` is supported.
"""
mfa_disabled: Optional[bool] = None
diff --git a/src/cloudflare/types/zero_trust/access/application_get_response.py b/src/cloudflare/types/zero_trust/access/application_get_response.py
index 6e024bb393d..07f245a1390 100644
--- a/src/cloudflare/types/zero_trust/access/application_get_response.py
+++ b/src/cloudflare/types/zero_trust/access/application_get_response.py
@@ -3068,10 +3068,10 @@ class InfrastructureApplicationPolicyMfaConfig(BaseModel):
Configures multi-factor authentication (MFA) settings for infrastructure applications.
"""
- allowed_authenticators: Optional[List[Literal["ssh_piv_key"]]] = None
+ allowed_authenticators: Optional[List[Literal["piv_key"]]] = None
"""Lists the MFA methods that users can authenticate with.
- For infrastructure applications, only `ssh_piv_key` is supported.
+ For infrastructure applications, only `piv_key` is supported.
"""
mfa_disabled: Optional[bool] = None
diff --git a/src/cloudflare/types/zero_trust/access/application_list_response.py b/src/cloudflare/types/zero_trust/access/application_list_response.py
index 9bf9d11f75a..d08bb9bc31b 100644
--- a/src/cloudflare/types/zero_trust/access/application_list_response.py
+++ b/src/cloudflare/types/zero_trust/access/application_list_response.py
@@ -3068,10 +3068,10 @@ class InfrastructureApplicationPolicyMfaConfig(BaseModel):
Configures multi-factor authentication (MFA) settings for infrastructure applications.
"""
- allowed_authenticators: Optional[List[Literal["ssh_piv_key"]]] = None
+ allowed_authenticators: Optional[List[Literal["piv_key"]]] = None
"""Lists the MFA methods that users can authenticate with.
- For infrastructure applications, only `ssh_piv_key` is supported.
+ For infrastructure applications, only `piv_key` is supported.
"""
mfa_disabled: Optional[bool] = None
diff --git a/src/cloudflare/types/zero_trust/access/application_update_params.py b/src/cloudflare/types/zero_trust/access/application_update_params.py
index 04d3ad5a066..d1917d8e54c 100644
--- a/src/cloudflare/types/zero_trust/access/application_update_params.py
+++ b/src/cloudflare/types/zero_trust/access/application_update_params.py
@@ -3039,10 +3039,10 @@ class InfrastructureApplicationPolicyMfaConfig(TypedDict, total=False):
Configures multi-factor authentication (MFA) settings for infrastructure applications.
"""
- allowed_authenticators: List[Literal["ssh_piv_key"]]
+ allowed_authenticators: List[Literal["piv_key"]]
"""Lists the MFA methods that users can authenticate with.
- For infrastructure applications, only `ssh_piv_key` is supported.
+ For infrastructure applications, only `piv_key` is supported.
"""
mfa_disabled: bool
diff --git a/src/cloudflare/types/zero_trust/access/application_update_response.py b/src/cloudflare/types/zero_trust/access/application_update_response.py
index e7020006b87..935cb9a3967 100644
--- a/src/cloudflare/types/zero_trust/access/application_update_response.py
+++ b/src/cloudflare/types/zero_trust/access/application_update_response.py
@@ -3068,10 +3068,10 @@ class InfrastructureApplicationPolicyMfaConfig(BaseModel):
Configures multi-factor authentication (MFA) settings for infrastructure applications.
"""
- allowed_authenticators: Optional[List[Literal["ssh_piv_key"]]] = None
+ allowed_authenticators: Optional[List[Literal["piv_key"]]] = None
"""Lists the MFA methods that users can authenticate with.
- For infrastructure applications, only `ssh_piv_key` is supported.
+ For infrastructure applications, only `piv_key` is supported.
"""
mfa_disabled: Optional[bool] = None
diff --git a/src/cloudflare/types/zero_trust/organization.py b/src/cloudflare/types/zero_trust/organization.py
index b4f96e14ab1..b50bb9f26f3 100644
--- a/src/cloudflare/types/zero_trust/organization.py
+++ b/src/cloudflare/types/zero_trust/organization.py
@@ -6,7 +6,7 @@
from ..._models import BaseModel
from .login_design import LoginDesign
-__all__ = ["Organization", "CustomPages", "MfaConfig", "MfaSSHPivKeyRequirements"]
+__all__ = ["Organization", "CustomPages", "MfaConfig", "MfaPivKeyRequirements"]
class CustomPages(BaseModel):
@@ -23,7 +23,7 @@ class CustomPages(BaseModel):
class MfaConfig(BaseModel):
"""Configures multi-factor authentication (MFA) settings for an organization."""
- allowed_authenticators: Optional[List[Literal["totp", "biometrics", "security_key", "ssh_piv_key"]]] = None
+ allowed_authenticators: Optional[List[Literal["totp", "biometrics", "security_key", "piv_key"]]] = None
"""Lists the MFA methods that users can authenticate with."""
amr_matching_session_duration: Optional[str] = None
@@ -45,8 +45,8 @@ class MfaConfig(BaseModel):
"""
-class MfaSSHPivKeyRequirements(BaseModel):
- """Configures SSH PIV key requirements for MFA using hardware security keys."""
+class MfaPivKeyRequirements(BaseModel):
+ """Configures PIV key requirements for MFA using hardware security keys."""
pin_policy: Optional[Literal["never", "once", "always"]] = None
"""Defines when a PIN is required to use the SSH key.
@@ -57,8 +57,8 @@ class MfaSSHPivKeyRequirements(BaseModel):
require_fips_device: Optional[bool] = None
"""
- Requires the SSH PIV key to be stored on a FIPS 140-2 Level 1 or higher
- validated device.
+ Requires the PIV key to be stored on a FIPS 140-2 Level 1 or higher validated
+ device.
"""
ssh_key_size: Optional[List[Literal[256, 384, 521, 2048, 3072, 4096]]] = None
@@ -128,18 +128,18 @@ class Organization(BaseModel):
mfa_config: Optional[MfaConfig] = None
"""Configures multi-factor authentication (MFA) settings for an organization."""
+ mfa_piv_key_requirements: Optional[MfaPivKeyRequirements] = None
+ """Configures PIV key requirements for MFA using hardware security keys."""
+
mfa_required_for_all_apps: Optional[bool] = None
"""Determines whether global MFA settings apply to applications by default.
The organization must have MFA enabled with at least one authentication method
and a session duration configured. Note: 'allowed_authenticators' cannot only
- contain 'ssh_piv_key' if the organization has any non-infrastructure
- applications because PIV keys are only compatible with infrastructure apps.
+ contain 'piv_key' if the organization has any non-infrastructure applications
+ because PIV keys are only compatible with infrastructure apps.
"""
- mfa_ssh_piv_key_requirements: Optional[MfaSSHPivKeyRequirements] = None
- """Configures SSH PIV key requirements for MFA using hardware security keys."""
-
name: Optional[str] = None
"""The name of your Zero Trust organization."""
diff --git a/src/cloudflare/types/zero_trust/organization_create_params.py b/src/cloudflare/types/zero_trust/organization_create_params.py
index 7ace8bedc86..28ae4b5321f 100644
--- a/src/cloudflare/types/zero_trust/organization_create_params.py
+++ b/src/cloudflare/types/zero_trust/organization_create_params.py
@@ -8,7 +8,7 @@
from ..._types import SequenceNotStr
from .login_design_param import LoginDesignParam
-__all__ = ["OrganizationCreateParams", "MfaConfig", "MfaSSHPivKeyRequirements"]
+__all__ = ["OrganizationCreateParams", "MfaConfig", "MfaPivKeyRequirements"]
class OrganizationCreateParams(TypedDict, total=False):
@@ -64,18 +64,18 @@ class OrganizationCreateParams(TypedDict, total=False):
mfa_config: MfaConfig
"""Configures multi-factor authentication (MFA) settings for an organization."""
+ mfa_piv_key_requirements: MfaPivKeyRequirements
+ """Configures PIV key requirements for MFA using hardware security keys."""
+
mfa_required_for_all_apps: bool
"""Determines whether global MFA settings apply to applications by default.
The organization must have MFA enabled with at least one authentication method
and a session duration configured. Note: 'allowed_authenticators' cannot only
- contain 'ssh_piv_key' if the organization has any non-infrastructure
- applications because PIV keys are only compatible with infrastructure apps.
+ contain 'piv_key' if the organization has any non-infrastructure applications
+ because PIV keys are only compatible with infrastructure apps.
"""
- mfa_ssh_piv_key_requirements: MfaSSHPivKeyRequirements
- """Configures SSH PIV key requirements for MFA using hardware security keys."""
-
session_duration: str
"""The amount of time that tokens issued for applications will be valid.
@@ -105,7 +105,7 @@ class OrganizationCreateParams(TypedDict, total=False):
class MfaConfig(TypedDict, total=False):
"""Configures multi-factor authentication (MFA) settings for an organization."""
- allowed_authenticators: List[Literal["totp", "biometrics", "security_key", "ssh_piv_key"]]
+ allowed_authenticators: List[Literal["totp", "biometrics", "security_key", "piv_key"]]
"""Lists the MFA methods that users can authenticate with."""
amr_matching_session_duration: str
@@ -127,8 +127,8 @@ class MfaConfig(TypedDict, total=False):
"""
-class MfaSSHPivKeyRequirements(TypedDict, total=False):
- """Configures SSH PIV key requirements for MFA using hardware security keys."""
+class MfaPivKeyRequirements(TypedDict, total=False):
+ """Configures PIV key requirements for MFA using hardware security keys."""
pin_policy: Literal["never", "once", "always"]
"""Defines when a PIN is required to use the SSH key.
@@ -139,8 +139,8 @@ class MfaSSHPivKeyRequirements(TypedDict, total=False):
require_fips_device: bool
"""
- Requires the SSH PIV key to be stored on a FIPS 140-2 Level 1 or higher
- validated device.
+ Requires the PIV key to be stored on a FIPS 140-2 Level 1 or higher validated
+ device.
"""
ssh_key_size: Iterable[Literal[256, 384, 521, 2048, 3072, 4096]]
diff --git a/src/cloudflare/types/zero_trust/organization_update_params.py b/src/cloudflare/types/zero_trust/organization_update_params.py
index 1405dd3ffb4..ab77fea8cd1 100644
--- a/src/cloudflare/types/zero_trust/organization_update_params.py
+++ b/src/cloudflare/types/zero_trust/organization_update_params.py
@@ -8,7 +8,7 @@
from ..._types import SequenceNotStr
from .login_design_param import LoginDesignParam
-__all__ = ["OrganizationUpdateParams", "CustomPages", "MfaConfig", "MfaSSHPivKeyRequirements"]
+__all__ = ["OrganizationUpdateParams", "CustomPages", "MfaConfig", "MfaPivKeyRequirements"]
class OrganizationUpdateParams(TypedDict, total=False):
@@ -63,18 +63,18 @@ class OrganizationUpdateParams(TypedDict, total=False):
mfa_config: MfaConfig
"""Configures multi-factor authentication (MFA) settings for an organization."""
+ mfa_piv_key_requirements: MfaPivKeyRequirements
+ """Configures PIV key requirements for MFA using hardware security keys."""
+
mfa_required_for_all_apps: bool
"""Determines whether global MFA settings apply to applications by default.
The organization must have MFA enabled with at least one authentication method
and a session duration configured. Note: 'allowed_authenticators' cannot only
- contain 'ssh_piv_key' if the organization has any non-infrastructure
- applications because PIV keys are only compatible with infrastructure apps.
+ contain 'piv_key' if the organization has any non-infrastructure applications
+ because PIV keys are only compatible with infrastructure apps.
"""
- mfa_ssh_piv_key_requirements: MfaSSHPivKeyRequirements
- """Configures SSH PIV key requirements for MFA using hardware security keys."""
-
name: str
"""The name of your Zero Trust organization."""
@@ -118,7 +118,7 @@ class CustomPages(TypedDict, total=False):
class MfaConfig(TypedDict, total=False):
"""Configures multi-factor authentication (MFA) settings for an organization."""
- allowed_authenticators: List[Literal["totp", "biometrics", "security_key", "ssh_piv_key"]]
+ allowed_authenticators: List[Literal["totp", "biometrics", "security_key", "piv_key"]]
"""Lists the MFA methods that users can authenticate with."""
amr_matching_session_duration: str
@@ -140,8 +140,8 @@ class MfaConfig(TypedDict, total=False):
"""
-class MfaSSHPivKeyRequirements(TypedDict, total=False):
- """Configures SSH PIV key requirements for MFA using hardware security keys."""
+class MfaPivKeyRequirements(TypedDict, total=False):
+ """Configures PIV key requirements for MFA using hardware security keys."""
pin_policy: Literal["never", "once", "always"]
"""Defines when a PIN is required to use the SSH key.
@@ -152,8 +152,8 @@ class MfaSSHPivKeyRequirements(TypedDict, total=False):
require_fips_device: bool
"""
- Requires the SSH PIV key to be stored on a FIPS 140-2 Level 1 or higher
- validated device.
+ Requires the PIV key to be stored on a FIPS 140-2 Level 1 or higher validated
+ device.
"""
ssh_key_size: Iterable[Literal[256, 384, 521, 2048, 3072, 4096]]
diff --git a/tests/api_resources/ai_gateway/billing/test_spending_limit.py b/tests/api_resources/ai_gateway/billing/test_spending_limit.py
index 78b1b9aad87..55637811211 100644
--- a/tests/api_resources/ai_gateway/billing/test_spending_limit.py
+++ b/tests/api_resources/ai_gateway/billing/test_spending_limit.py
@@ -11,6 +11,8 @@
from tests.utils import assert_matches_type
from cloudflare.types.ai_gateway.billing import SpendingLimitGetResponse
+# pyright: reportDeprecated=false
+
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -20,23 +22,26 @@ class TestSpendingLimit:
@pytest.mark.skip(reason="HTTP 404 error from prism")
@parametrize
def test_method_create(self, client: Cloudflare) -> None:
- spending_limit = client.ai_gateway.billing.spending_limit.create(
- account_id="account_id",
- amount=10000,
- duration="monthly",
- strategy="fixed",
- )
+ with pytest.warns(DeprecationWarning):
+ spending_limit = client.ai_gateway.billing.spending_limit.create(
+ account_id="account_id",
+ amount=10000,
+ duration="monthly",
+ strategy="fixed",
+ )
+
assert_matches_type(object, spending_limit, path=["response"])
@pytest.mark.skip(reason="HTTP 404 error from prism")
@parametrize
def test_raw_response_create(self, client: Cloudflare) -> None:
- response = client.ai_gateway.billing.spending_limit.with_raw_response.create(
- account_id="account_id",
- amount=10000,
- duration="monthly",
- strategy="fixed",
- )
+ with pytest.warns(DeprecationWarning):
+ response = client.ai_gateway.billing.spending_limit.with_raw_response.create(
+ account_id="account_id",
+ amount=10000,
+ duration="monthly",
+ strategy="fixed",
+ )
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -46,30 +51,32 @@ def test_raw_response_create(self, client: Cloudflare) -> None:
@pytest.mark.skip(reason="HTTP 404 error from prism")
@parametrize
def test_streaming_response_create(self, client: Cloudflare) -> None:
- with client.ai_gateway.billing.spending_limit.with_streaming_response.create(
- account_id="account_id",
- amount=10000,
- duration="monthly",
- strategy="fixed",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ with pytest.warns(DeprecationWarning):
+ with client.ai_gateway.billing.spending_limit.with_streaming_response.create(
+ account_id="account_id",
+ amount=10000,
+ duration="monthly",
+ strategy="fixed",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- spending_limit = response.parse()
- assert_matches_type(object, spending_limit, path=["response"])
+ spending_limit = response.parse()
+ assert_matches_type(object, spending_limit, path=["response"])
assert cast(Any, response.is_closed) is True
@pytest.mark.skip(reason="HTTP 404 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.ai_gateway.billing.spending_limit.with_raw_response.create(
- account_id="",
- amount=10000,
- duration="monthly",
- strategy="fixed",
- )
+ with pytest.warns(DeprecationWarning):
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ client.ai_gateway.billing.spending_limit.with_raw_response.create(
+ account_id="",
+ amount=10000,
+ duration="monthly",
+ strategy="fixed",
+ )
@pytest.mark.skip(reason="HTTP 404 error from prism")
@parametrize
@@ -164,23 +171,26 @@ class TestAsyncSpendingLimit:
@pytest.mark.skip(reason="HTTP 404 error from prism")
@parametrize
async def test_method_create(self, async_client: AsyncCloudflare) -> None:
- spending_limit = await async_client.ai_gateway.billing.spending_limit.create(
- account_id="account_id",
- amount=10000,
- duration="monthly",
- strategy="fixed",
- )
+ with pytest.warns(DeprecationWarning):
+ spending_limit = await async_client.ai_gateway.billing.spending_limit.create(
+ account_id="account_id",
+ amount=10000,
+ duration="monthly",
+ strategy="fixed",
+ )
+
assert_matches_type(object, spending_limit, path=["response"])
@pytest.mark.skip(reason="HTTP 404 error from prism")
@parametrize
async def test_raw_response_create(self, async_client: AsyncCloudflare) -> None:
- response = await async_client.ai_gateway.billing.spending_limit.with_raw_response.create(
- account_id="account_id",
- amount=10000,
- duration="monthly",
- strategy="fixed",
- )
+ with pytest.warns(DeprecationWarning):
+ response = await async_client.ai_gateway.billing.spending_limit.with_raw_response.create(
+ account_id="account_id",
+ amount=10000,
+ duration="monthly",
+ strategy="fixed",
+ )
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -190,30 +200,32 @@ async def test_raw_response_create(self, async_client: AsyncCloudflare) -> None:
@pytest.mark.skip(reason="HTTP 404 error from prism")
@parametrize
async def test_streaming_response_create(self, async_client: AsyncCloudflare) -> None:
- async with async_client.ai_gateway.billing.spending_limit.with_streaming_response.create(
- account_id="account_id",
- amount=10000,
- duration="monthly",
- strategy="fixed",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ with pytest.warns(DeprecationWarning):
+ async with async_client.ai_gateway.billing.spending_limit.with_streaming_response.create(
+ account_id="account_id",
+ amount=10000,
+ duration="monthly",
+ strategy="fixed",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- spending_limit = await response.parse()
- assert_matches_type(object, spending_limit, path=["response"])
+ spending_limit = await response.parse()
+ assert_matches_type(object, spending_limit, path=["response"])
assert cast(Any, response.is_closed) is True
@pytest.mark.skip(reason="HTTP 404 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.ai_gateway.billing.spending_limit.with_raw_response.create(
- account_id="",
- amount=10000,
- duration="monthly",
- strategy="fixed",
- )
+ with pytest.warns(DeprecationWarning):
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ await async_client.ai_gateway.billing.spending_limit.with_raw_response.create(
+ account_id="",
+ amount=10000,
+ duration="monthly",
+ strategy="fixed",
+ )
@pytest.mark.skip(reason="HTTP 404 error from prism")
@parametrize
diff --git a/tests/api_resources/email_auth/__init__.py b/tests/api_resources/email_auth/__init__.py
new file mode 100644
index 00000000000..fd8019a9a1a
--- /dev/null
+++ b/tests/api_resources/email_auth/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/email_auth/spf/__init__.py b/tests/api_resources/email_auth/spf/__init__.py
new file mode 100644
index 00000000000..fd8019a9a1a
--- /dev/null
+++ b/tests/api_resources/email_auth/spf/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/email_auth/spf/test_inspect.py b/tests/api_resources/email_auth/spf/test_inspect.py
new file mode 100644
index 00000000000..c18654d8d3a
--- /dev/null
+++ b/tests/api_resources/email_auth/spf/test_inspect.py
@@ -0,0 +1,108 @@
+# 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.email_auth.spf import InspectGetResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestInspect:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_get(self, client: Cloudflare) -> None:
+ inspect = client.email_auth.spf.inspect.get(
+ zone_id="023e105f4ecef8ad9ca31a8372d0c353",
+ id="id",
+ )
+ assert_matches_type(Optional[InspectGetResponse], inspect, path=["response"])
+
+ @parametrize
+ def test_raw_response_get(self, client: Cloudflare) -> None:
+ response = client.email_auth.spf.inspect.with_raw_response.get(
+ zone_id="023e105f4ecef8ad9ca31a8372d0c353",
+ id="id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ inspect = response.parse()
+ assert_matches_type(Optional[InspectGetResponse], inspect, path=["response"])
+
+ @parametrize
+ def test_streaming_response_get(self, client: Cloudflare) -> None:
+ with client.email_auth.spf.inspect.with_streaming_response.get(
+ zone_id="023e105f4ecef8ad9ca31a8372d0c353",
+ id="id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ inspect = response.parse()
+ assert_matches_type(Optional[InspectGetResponse], inspect, 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 `zone_id` but received ''"):
+ client.email_auth.spf.inspect.with_raw_response.get(
+ zone_id="",
+ id="id",
+ )
+
+
+class TestAsyncInspect:
+ 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:
+ inspect = await async_client.email_auth.spf.inspect.get(
+ zone_id="023e105f4ecef8ad9ca31a8372d0c353",
+ id="id",
+ )
+ assert_matches_type(Optional[InspectGetResponse], inspect, path=["response"])
+
+ @parametrize
+ async def test_raw_response_get(self, async_client: AsyncCloudflare) -> None:
+ response = await async_client.email_auth.spf.inspect.with_raw_response.get(
+ zone_id="023e105f4ecef8ad9ca31a8372d0c353",
+ id="id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ inspect = await response.parse()
+ assert_matches_type(Optional[InspectGetResponse], inspect, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_get(self, async_client: AsyncCloudflare) -> None:
+ async with async_client.email_auth.spf.inspect.with_streaming_response.get(
+ zone_id="023e105f4ecef8ad9ca31a8372d0c353",
+ id="id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ inspect = await response.parse()
+ assert_matches_type(Optional[InspectGetResponse], inspect, 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 `zone_id` but received ''"):
+ await async_client.email_auth.spf.inspect.with_raw_response.get(
+ zone_id="",
+ id="id",
+ )
diff --git a/tests/api_resources/email_auth/test_dmarc_reports.py b/tests/api_resources/email_auth/test_dmarc_reports.py
new file mode 100644
index 00000000000..85d7655496e
--- /dev/null
+++ b/tests/api_resources/email_auth/test_dmarc_reports.py
@@ -0,0 +1,194 @@
+# 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.email_auth import DMARCReportGetResponse, DMARCReportEditResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestDMARCReports:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_edit(self, client: Cloudflare) -> None:
+ dmarc_report = client.email_auth.dmarc_reports.edit(
+ zone_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+ assert_matches_type(Optional[DMARCReportEditResponse], dmarc_report, path=["response"])
+
+ @parametrize
+ def test_method_edit_with_all_params(self, client: Cloudflare) -> None:
+ dmarc_report = client.email_auth.dmarc_reports.edit(
+ zone_id="023e105f4ecef8ad9ca31a8372d0c353",
+ enabled=True,
+ skip_wizard=False,
+ )
+ assert_matches_type(Optional[DMARCReportEditResponse], dmarc_report, path=["response"])
+
+ @parametrize
+ def test_raw_response_edit(self, client: Cloudflare) -> None:
+ response = client.email_auth.dmarc_reports.with_raw_response.edit(
+ zone_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ dmarc_report = response.parse()
+ assert_matches_type(Optional[DMARCReportEditResponse], dmarc_report, path=["response"])
+
+ @parametrize
+ def test_streaming_response_edit(self, client: Cloudflare) -> None:
+ with client.email_auth.dmarc_reports.with_streaming_response.edit(
+ zone_id="023e105f4ecef8ad9ca31a8372d0c353",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ dmarc_report = response.parse()
+ assert_matches_type(Optional[DMARCReportEditResponse], dmarc_report, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_edit(self, client: Cloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `zone_id` but received ''"):
+ client.email_auth.dmarc_reports.with_raw_response.edit(
+ zone_id="",
+ )
+
+ @parametrize
+ def test_method_get(self, client: Cloudflare) -> None:
+ dmarc_report = client.email_auth.dmarc_reports.get(
+ zone_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+ assert_matches_type(Optional[DMARCReportGetResponse], dmarc_report, path=["response"])
+
+ @parametrize
+ def test_raw_response_get(self, client: Cloudflare) -> None:
+ response = client.email_auth.dmarc_reports.with_raw_response.get(
+ zone_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ dmarc_report = response.parse()
+ assert_matches_type(Optional[DMARCReportGetResponse], dmarc_report, path=["response"])
+
+ @parametrize
+ def test_streaming_response_get(self, client: Cloudflare) -> None:
+ with client.email_auth.dmarc_reports.with_streaming_response.get(
+ zone_id="023e105f4ecef8ad9ca31a8372d0c353",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ dmarc_report = response.parse()
+ assert_matches_type(Optional[DMARCReportGetResponse], dmarc_report, 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 `zone_id` but received ''"):
+ client.email_auth.dmarc_reports.with_raw_response.get(
+ zone_id="",
+ )
+
+
+class TestAsyncDMARCReports:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_edit(self, async_client: AsyncCloudflare) -> None:
+ dmarc_report = await async_client.email_auth.dmarc_reports.edit(
+ zone_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+ assert_matches_type(Optional[DMARCReportEditResponse], dmarc_report, path=["response"])
+
+ @parametrize
+ async def test_method_edit_with_all_params(self, async_client: AsyncCloudflare) -> None:
+ dmarc_report = await async_client.email_auth.dmarc_reports.edit(
+ zone_id="023e105f4ecef8ad9ca31a8372d0c353",
+ enabled=True,
+ skip_wizard=False,
+ )
+ assert_matches_type(Optional[DMARCReportEditResponse], dmarc_report, path=["response"])
+
+ @parametrize
+ async def test_raw_response_edit(self, async_client: AsyncCloudflare) -> None:
+ response = await async_client.email_auth.dmarc_reports.with_raw_response.edit(
+ zone_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ dmarc_report = await response.parse()
+ assert_matches_type(Optional[DMARCReportEditResponse], dmarc_report, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_edit(self, async_client: AsyncCloudflare) -> None:
+ async with async_client.email_auth.dmarc_reports.with_streaming_response.edit(
+ zone_id="023e105f4ecef8ad9ca31a8372d0c353",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ dmarc_report = await response.parse()
+ assert_matches_type(Optional[DMARCReportEditResponse], dmarc_report, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_edit(self, async_client: AsyncCloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `zone_id` but received ''"):
+ await async_client.email_auth.dmarc_reports.with_raw_response.edit(
+ zone_id="",
+ )
+
+ @parametrize
+ async def test_method_get(self, async_client: AsyncCloudflare) -> None:
+ dmarc_report = await async_client.email_auth.dmarc_reports.get(
+ zone_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+ assert_matches_type(Optional[DMARCReportGetResponse], dmarc_report, path=["response"])
+
+ @parametrize
+ async def test_raw_response_get(self, async_client: AsyncCloudflare) -> None:
+ response = await async_client.email_auth.dmarc_reports.with_raw_response.get(
+ zone_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ dmarc_report = await response.parse()
+ assert_matches_type(Optional[DMARCReportGetResponse], dmarc_report, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_get(self, async_client: AsyncCloudflare) -> None:
+ async with async_client.email_auth.dmarc_reports.with_streaming_response.get(
+ zone_id="023e105f4ecef8ad9ca31a8372d0c353",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ dmarc_report = await response.parse()
+ assert_matches_type(Optional[DMARCReportGetResponse], dmarc_report, 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 `zone_id` but received ''"):
+ await async_client.email_auth.dmarc_reports.with_raw_response.get(
+ zone_id="",
+ )
diff --git a/tests/api_resources/email_routing/test_rules.py b/tests/api_resources/email_routing/test_rules.py
index 33b96b74828..03090d5bfc2 100644
--- a/tests/api_resources/email_routing/test_rules.py
+++ b/tests/api_resources/email_routing/test_rules.py
@@ -9,7 +9,6 @@
from cloudflare import Cloudflare, AsyncCloudflare
from tests.utils import assert_matches_type
-from cloudflare.pagination import SyncV4PagePaginationArray, AsyncV4PagePaginationArray
from cloudflare.types.email_routing import EmailRoutingRule
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -169,54 +168,6 @@ def test_path_params_update(self, client: Cloudflare) -> None:
matchers=[{"type": "literal"}],
)
- @parametrize
- def test_method_list(self, client: Cloudflare) -> None:
- rule = client.email_routing.rules.list(
- zone_id="023e105f4ecef8ad9ca31a8372d0c353",
- )
- assert_matches_type(SyncV4PagePaginationArray[EmailRoutingRule], rule, path=["response"])
-
- @parametrize
- def test_method_list_with_all_params(self, client: Cloudflare) -> None:
- rule = client.email_routing.rules.list(
- zone_id="023e105f4ecef8ad9ca31a8372d0c353",
- enabled=True,
- page=1,
- per_page=5,
- )
- assert_matches_type(SyncV4PagePaginationArray[EmailRoutingRule], rule, path=["response"])
-
- @parametrize
- def test_raw_response_list(self, client: Cloudflare) -> None:
- response = client.email_routing.rules.with_raw_response.list(
- zone_id="023e105f4ecef8ad9ca31a8372d0c353",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- rule = response.parse()
- assert_matches_type(SyncV4PagePaginationArray[EmailRoutingRule], rule, path=["response"])
-
- @parametrize
- def test_streaming_response_list(self, client: Cloudflare) -> None:
- with client.email_routing.rules.with_streaming_response.list(
- zone_id="023e105f4ecef8ad9ca31a8372d0c353",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- rule = response.parse()
- assert_matches_type(SyncV4PagePaginationArray[EmailRoutingRule], rule, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @parametrize
- def test_path_params_list(self, client: Cloudflare) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `zone_id` but received ''"):
- client.email_routing.rules.with_raw_response.list(
- zone_id="",
- )
-
@parametrize
def test_method_delete(self, client: Cloudflare) -> None:
rule = client.email_routing.rules.delete(
@@ -470,54 +421,6 @@ async def test_path_params_update(self, async_client: AsyncCloudflare) -> None:
matchers=[{"type": "literal"}],
)
- @parametrize
- async def test_method_list(self, async_client: AsyncCloudflare) -> None:
- rule = await async_client.email_routing.rules.list(
- zone_id="023e105f4ecef8ad9ca31a8372d0c353",
- )
- assert_matches_type(AsyncV4PagePaginationArray[EmailRoutingRule], rule, path=["response"])
-
- @parametrize
- async def test_method_list_with_all_params(self, async_client: AsyncCloudflare) -> None:
- rule = await async_client.email_routing.rules.list(
- zone_id="023e105f4ecef8ad9ca31a8372d0c353",
- enabled=True,
- page=1,
- per_page=5,
- )
- assert_matches_type(AsyncV4PagePaginationArray[EmailRoutingRule], rule, path=["response"])
-
- @parametrize
- async def test_raw_response_list(self, async_client: AsyncCloudflare) -> None:
- response = await async_client.email_routing.rules.with_raw_response.list(
- zone_id="023e105f4ecef8ad9ca31a8372d0c353",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- rule = await response.parse()
- assert_matches_type(AsyncV4PagePaginationArray[EmailRoutingRule], rule, path=["response"])
-
- @parametrize
- async def test_streaming_response_list(self, async_client: AsyncCloudflare) -> None:
- async with async_client.email_routing.rules.with_streaming_response.list(
- zone_id="023e105f4ecef8ad9ca31a8372d0c353",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- rule = await response.parse()
- assert_matches_type(AsyncV4PagePaginationArray[EmailRoutingRule], rule, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @parametrize
- async def test_path_params_list(self, async_client: AsyncCloudflare) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `zone_id` but received ''"):
- await async_client.email_routing.rules.with_raw_response.list(
- zone_id="",
- )
-
@parametrize
async def test_method_delete(self, async_client: AsyncCloudflare) -> None:
rule = await async_client.email_routing.rules.delete(
diff --git a/tests/api_resources/email_security/investigate/test_move.py b/tests/api_resources/email_security/investigate/test_move.py
index cf738cea491..19eadebcef0 100644
--- a/tests/api_resources/email_security/investigate/test_move.py
+++ b/tests/api_resources/email_security/investigate/test_move.py
@@ -30,6 +30,16 @@ def test_method_create(self, client: Cloudflare) -> None:
)
assert_matches_type(SyncSinglePage[MoveCreateResponse], move, path=["response"])
+ @parametrize
+ def test_method_create_with_all_params(self, client: Cloudflare) -> None:
+ move = client.email_security.investigate.move.create(
+ investigate_id="4Njp3P0STMz2c02Q-2024-01-05T10:00:00-12345678",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ destination="Inbox",
+ expected_disposition="MALICIOUS",
+ )
+ assert_matches_type(SyncSinglePage[MoveCreateResponse], move, path=["response"])
+
@parametrize
def test_raw_response_create(self, client: Cloudflare) -> None:
response = client.email_security.investigate.move.with_raw_response.create(
@@ -87,6 +97,7 @@ def test_method_bulk_with_all_params(self, client: Cloudflare) -> None:
move = client.email_security.investigate.move.bulk(
account_id="023e105f4ecef8ad9ca31a8372d0c353",
destination="Inbox",
+ expected_disposition="MALICIOUS",
ids=["4Njp3P0STMz2c02Q-2024-01-05T10:00:00-12345678"],
postfix_ids=["4Njp3P0STMz2c02Q"],
)
@@ -141,6 +152,16 @@ async def test_method_create(self, async_client: AsyncCloudflare) -> None:
)
assert_matches_type(AsyncSinglePage[MoveCreateResponse], move, path=["response"])
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncCloudflare) -> None:
+ move = await async_client.email_security.investigate.move.create(
+ investigate_id="4Njp3P0STMz2c02Q-2024-01-05T10:00:00-12345678",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ destination="Inbox",
+ expected_disposition="MALICIOUS",
+ )
+ assert_matches_type(AsyncSinglePage[MoveCreateResponse], move, path=["response"])
+
@parametrize
async def test_raw_response_create(self, async_client: AsyncCloudflare) -> None:
response = await async_client.email_security.investigate.move.with_raw_response.create(
@@ -198,6 +219,7 @@ async def test_method_bulk_with_all_params(self, async_client: AsyncCloudflare)
move = await async_client.email_security.investigate.move.bulk(
account_id="023e105f4ecef8ad9ca31a8372d0c353",
destination="Inbox",
+ expected_disposition="MALICIOUS",
ids=["4Njp3P0STMz2c02Q-2024-01-05T10:00:00-12345678"],
postfix_ids=["4Njp3P0STMz2c02Q"],
)
diff --git a/tests/api_resources/magic_transit/test_gre_tunnels.py b/tests/api_resources/magic_transit/test_gre_tunnels.py
index 95128794a5d..dc8a2757d5f 100644
--- a/tests/api_resources/magic_transit/test_gre_tunnels.py
+++ b/tests/api_resources/magic_transit/test_gre_tunnels.py
@@ -48,7 +48,9 @@ def test_method_create_with_all_params(self, client: Cloudflare) -> None:
automatic_return_routing=True,
bgp={
"customer_asn": 0,
+ "export_filter_id": "a1b2c3d4e5f647890a1b2c3d4e5f6789",
"extra_prefixes": ["string"],
+ "import_filter_id": "a1b2c3d4e5f647890a1b2c3d4e5f6789",
"md5_key": "md5_key",
},
description="Tunnel for ISP X",
@@ -456,7 +458,9 @@ async def test_method_create_with_all_params(self, async_client: AsyncCloudflare
automatic_return_routing=True,
bgp={
"customer_asn": 0,
+ "export_filter_id": "a1b2c3d4e5f647890a1b2c3d4e5f6789",
"extra_prefixes": ["string"],
+ "import_filter_id": "a1b2c3d4e5f647890a1b2c3d4e5f6789",
"md5_key": "md5_key",
},
description="Tunnel for ISP X",
diff --git a/tests/api_resources/magic_transit/test_ipsec_tunnels.py b/tests/api_resources/magic_transit/test_ipsec_tunnels.py
index a9c532cf3eb..d7d4aa22b0e 100644
--- a/tests/api_resources/magic_transit/test_ipsec_tunnels.py
+++ b/tests/api_resources/magic_transit/test_ipsec_tunnels.py
@@ -46,7 +46,9 @@ def test_method_create_with_all_params(self, client: Cloudflare) -> None:
automatic_return_routing=True,
bgp={
"customer_asn": 0,
+ "export_filter_id": "a1b2c3d4e5f647890a1b2c3d4e5f6789",
"extra_prefixes": ["string"],
+ "import_filter_id": "a1b2c3d4e5f647890a1b2c3d4e5f6789",
"md5_key": "md5_key",
},
custom_remote_identities={"fqdn_id": "fqdn_id"},
@@ -130,7 +132,9 @@ def test_method_update_with_all_params(self, client: Cloudflare) -> None:
automatic_return_routing=True,
bgp={
"customer_asn": 0,
+ "export_filter_id": "a1b2c3d4e5f647890a1b2c3d4e5f6789",
"extra_prefixes": ["string"],
+ "import_filter_id": "a1b2c3d4e5f647890a1b2c3d4e5f6789",
"md5_key": "md5_key",
},
custom_remote_identities={"fqdn_id": "fqdn_id"},
@@ -576,7 +580,9 @@ async def test_method_create_with_all_params(self, async_client: AsyncCloudflare
automatic_return_routing=True,
bgp={
"customer_asn": 0,
+ "export_filter_id": "a1b2c3d4e5f647890a1b2c3d4e5f6789",
"extra_prefixes": ["string"],
+ "import_filter_id": "a1b2c3d4e5f647890a1b2c3d4e5f6789",
"md5_key": "md5_key",
},
custom_remote_identities={"fqdn_id": "fqdn_id"},
@@ -660,7 +666,9 @@ async def test_method_update_with_all_params(self, async_client: AsyncCloudflare
automatic_return_routing=True,
bgp={
"customer_asn": 0,
+ "export_filter_id": "a1b2c3d4e5f647890a1b2c3d4e5f6789",
"extra_prefixes": ["string"],
+ "import_filter_id": "a1b2c3d4e5f647890a1b2c3d4e5f6789",
"md5_key": "md5_key",
},
custom_remote_identities={"fqdn_id": "fqdn_id"},
diff --git a/tests/api_resources/moq/__init__.py b/tests/api_resources/moq/__init__.py
new file mode 100644
index 00000000000..fd8019a9a1a
--- /dev/null
+++ b/tests/api_resources/moq/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/moq/relays/__init__.py b/tests/api_resources/moq/relays/__init__.py
new file mode 100644
index 00000000000..fd8019a9a1a
--- /dev/null
+++ b/tests/api_resources/moq/relays/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/moq/relays/test_tokens.py b/tests/api_resources/moq/relays/test_tokens.py
new file mode 100644
index 00000000000..f39caa3990e
--- /dev/null
+++ b/tests/api_resources/moq/relays/test_tokens.py
@@ -0,0 +1,130 @@
+# 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.moq.relays import TokenRotateResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestTokens:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_rotate(self, client: Cloudflare) -> None:
+ token = client.moq.relays.tokens.rotate(
+ relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ type="publish_subscribe",
+ )
+ assert_matches_type(Optional[TokenRotateResponse], token, path=["response"])
+
+ @parametrize
+ def test_raw_response_rotate(self, client: Cloudflare) -> None:
+ response = client.moq.relays.tokens.with_raw_response.rotate(
+ relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ type="publish_subscribe",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ token = response.parse()
+ assert_matches_type(Optional[TokenRotateResponse], token, path=["response"])
+
+ @parametrize
+ def test_streaming_response_rotate(self, client: Cloudflare) -> None:
+ with client.moq.relays.tokens.with_streaming_response.rotate(
+ relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ type="publish_subscribe",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ token = response.parse()
+ assert_matches_type(Optional[TokenRotateResponse], token, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_rotate(self, client: Cloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ client.moq.relays.tokens.with_raw_response.rotate(
+ relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890",
+ account_id="",
+ type="publish_subscribe",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `relay_id` but received ''"):
+ client.moq.relays.tokens.with_raw_response.rotate(
+ relay_id="",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ type="publish_subscribe",
+ )
+
+
+class TestAsyncTokens:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_rotate(self, async_client: AsyncCloudflare) -> None:
+ token = await async_client.moq.relays.tokens.rotate(
+ relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ type="publish_subscribe",
+ )
+ assert_matches_type(Optional[TokenRotateResponse], token, path=["response"])
+
+ @parametrize
+ async def test_raw_response_rotate(self, async_client: AsyncCloudflare) -> None:
+ response = await async_client.moq.relays.tokens.with_raw_response.rotate(
+ relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ type="publish_subscribe",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ token = await response.parse()
+ assert_matches_type(Optional[TokenRotateResponse], token, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_rotate(self, async_client: AsyncCloudflare) -> None:
+ async with async_client.moq.relays.tokens.with_streaming_response.rotate(
+ relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ type="publish_subscribe",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ token = await response.parse()
+ assert_matches_type(Optional[TokenRotateResponse], token, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_rotate(self, async_client: AsyncCloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ await async_client.moq.relays.tokens.with_raw_response.rotate(
+ relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890",
+ account_id="",
+ type="publish_subscribe",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `relay_id` but received ''"):
+ await async_client.moq.relays.tokens.with_raw_response.rotate(
+ relay_id="",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ type="publish_subscribe",
+ )
diff --git a/tests/api_resources/moq/test_relays.py b/tests/api_resources/moq/test_relays.py
new file mode 100644
index 00000000000..ca73a9d4df8
--- /dev/null
+++ b/tests/api_resources/moq/test_relays.py
@@ -0,0 +1,539 @@
+# 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._utils import parse_datetime
+from cloudflare.types.moq import (
+ RelayGetResponse,
+ RelayListResponse,
+ RelayCreateResponse,
+ RelayUpdateResponse,
+)
+from cloudflare.pagination import SyncSinglePage, AsyncSinglePage
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestRelays:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_create(self, client: Cloudflare) -> None:
+ relay = client.moq.relays.create(
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ name="Production Live Stream",
+ )
+ assert_matches_type(Optional[RelayCreateResponse], relay, path=["response"])
+
+ @parametrize
+ def test_raw_response_create(self, client: Cloudflare) -> None:
+ response = client.moq.relays.with_raw_response.create(
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ name="Production Live Stream",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ relay = response.parse()
+ assert_matches_type(Optional[RelayCreateResponse], relay, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create(self, client: Cloudflare) -> None:
+ with client.moq.relays.with_streaming_response.create(
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ name="Production Live Stream",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ relay = response.parse()
+ assert_matches_type(Optional[RelayCreateResponse], relay, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @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.moq.relays.with_raw_response.create(
+ account_id="",
+ name="Production Live Stream",
+ )
+
+ @parametrize
+ def test_method_update(self, client: Cloudflare) -> None:
+ relay = client.moq.relays.update(
+ relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+ assert_matches_type(Optional[RelayUpdateResponse], relay, path=["response"])
+
+ @parametrize
+ def test_method_update_with_all_params(self, client: Cloudflare) -> None:
+ relay = client.moq.relays.update(
+ relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ config={
+ "lingering_subscribe": {
+ "enabled": True,
+ "max_timeout_ms": 0,
+ },
+ "origin_fallback": {
+ "enabled": True,
+ "origins": [{"url": "url"}],
+ },
+ },
+ name="name",
+ )
+ assert_matches_type(Optional[RelayUpdateResponse], relay, path=["response"])
+
+ @parametrize
+ def test_raw_response_update(self, client: Cloudflare) -> None:
+ response = client.moq.relays.with_raw_response.update(
+ relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ relay = response.parse()
+ assert_matches_type(Optional[RelayUpdateResponse], relay, path=["response"])
+
+ @parametrize
+ def test_streaming_response_update(self, client: Cloudflare) -> None:
+ with client.moq.relays.with_streaming_response.update(
+ relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ relay = response.parse()
+ assert_matches_type(Optional[RelayUpdateResponse], relay, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_update(self, client: Cloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ client.moq.relays.with_raw_response.update(
+ relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890",
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `relay_id` but received ''"):
+ client.moq.relays.with_raw_response.update(
+ relay_id="",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+
+ @parametrize
+ def test_method_list(self, client: Cloudflare) -> None:
+ relay = client.moq.relays.list(
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+ assert_matches_type(SyncSinglePage[RelayListResponse], relay, path=["response"])
+
+ @parametrize
+ def test_method_list_with_all_params(self, client: Cloudflare) -> None:
+ relay = client.moq.relays.list(
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ asc=True,
+ created_after=parse_datetime("2026-03-27T15:00:00Z"),
+ created_before=parse_datetime("2026-03-27T15:00:00Z"),
+ per_page=50,
+ )
+ assert_matches_type(SyncSinglePage[RelayListResponse], relay, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: Cloudflare) -> None:
+ response = client.moq.relays.with_raw_response.list(
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ relay = response.parse()
+ assert_matches_type(SyncSinglePage[RelayListResponse], relay, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: Cloudflare) -> None:
+ with client.moq.relays.with_streaming_response.list(
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ relay = response.parse()
+ assert_matches_type(SyncSinglePage[RelayListResponse], relay, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @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.moq.relays.with_raw_response.list(
+ account_id="",
+ )
+
+ @parametrize
+ def test_method_delete(self, client: Cloudflare) -> None:
+ relay = client.moq.relays.delete(
+ relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+ assert_matches_type(object, relay, path=["response"])
+
+ @parametrize
+ def test_raw_response_delete(self, client: Cloudflare) -> None:
+ response = client.moq.relays.with_raw_response.delete(
+ relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ relay = response.parse()
+ assert_matches_type(object, relay, path=["response"])
+
+ @parametrize
+ def test_streaming_response_delete(self, client: Cloudflare) -> None:
+ with client.moq.relays.with_streaming_response.delete(
+ relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ relay = response.parse()
+ assert_matches_type(object, relay, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @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.moq.relays.with_raw_response.delete(
+ relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890",
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `relay_id` but received ''"):
+ client.moq.relays.with_raw_response.delete(
+ relay_id="",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+
+ @parametrize
+ def test_method_get(self, client: Cloudflare) -> None:
+ relay = client.moq.relays.get(
+ relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+ assert_matches_type(Optional[RelayGetResponse], relay, path=["response"])
+
+ @parametrize
+ def test_raw_response_get(self, client: Cloudflare) -> None:
+ response = client.moq.relays.with_raw_response.get(
+ relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ relay = response.parse()
+ assert_matches_type(Optional[RelayGetResponse], relay, path=["response"])
+
+ @parametrize
+ def test_streaming_response_get(self, client: Cloudflare) -> None:
+ with client.moq.relays.with_streaming_response.get(
+ relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ relay = response.parse()
+ assert_matches_type(Optional[RelayGetResponse], relay, 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.moq.relays.with_raw_response.get(
+ relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890",
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `relay_id` but received ''"):
+ client.moq.relays.with_raw_response.get(
+ relay_id="",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+
+
+class TestAsyncRelays:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @parametrize
+ async def test_method_create(self, async_client: AsyncCloudflare) -> None:
+ relay = await async_client.moq.relays.create(
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ name="Production Live Stream",
+ )
+ assert_matches_type(Optional[RelayCreateResponse], relay, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncCloudflare) -> None:
+ response = await async_client.moq.relays.with_raw_response.create(
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ name="Production Live Stream",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ relay = await response.parse()
+ assert_matches_type(Optional[RelayCreateResponse], relay, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncCloudflare) -> None:
+ async with async_client.moq.relays.with_streaming_response.create(
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ name="Production Live Stream",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ relay = await response.parse()
+ assert_matches_type(Optional[RelayCreateResponse], relay, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @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.moq.relays.with_raw_response.create(
+ account_id="",
+ name="Production Live Stream",
+ )
+
+ @parametrize
+ async def test_method_update(self, async_client: AsyncCloudflare) -> None:
+ relay = await async_client.moq.relays.update(
+ relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+ assert_matches_type(Optional[RelayUpdateResponse], relay, path=["response"])
+
+ @parametrize
+ async def test_method_update_with_all_params(self, async_client: AsyncCloudflare) -> None:
+ relay = await async_client.moq.relays.update(
+ relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ config={
+ "lingering_subscribe": {
+ "enabled": True,
+ "max_timeout_ms": 0,
+ },
+ "origin_fallback": {
+ "enabled": True,
+ "origins": [{"url": "url"}],
+ },
+ },
+ name="name",
+ )
+ assert_matches_type(Optional[RelayUpdateResponse], relay, path=["response"])
+
+ @parametrize
+ async def test_raw_response_update(self, async_client: AsyncCloudflare) -> None:
+ response = await async_client.moq.relays.with_raw_response.update(
+ relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ relay = await response.parse()
+ assert_matches_type(Optional[RelayUpdateResponse], relay, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_update(self, async_client: AsyncCloudflare) -> None:
+ async with async_client.moq.relays.with_streaming_response.update(
+ relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ relay = await response.parse()
+ assert_matches_type(Optional[RelayUpdateResponse], relay, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_update(self, async_client: AsyncCloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ await async_client.moq.relays.with_raw_response.update(
+ relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890",
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `relay_id` but received ''"):
+ await async_client.moq.relays.with_raw_response.update(
+ relay_id="",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncCloudflare) -> None:
+ relay = await async_client.moq.relays.list(
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+ assert_matches_type(AsyncSinglePage[RelayListResponse], relay, path=["response"])
+
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncCloudflare) -> None:
+ relay = await async_client.moq.relays.list(
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ asc=True,
+ created_after=parse_datetime("2026-03-27T15:00:00Z"),
+ created_before=parse_datetime("2026-03-27T15:00:00Z"),
+ per_page=50,
+ )
+ assert_matches_type(AsyncSinglePage[RelayListResponse], relay, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncCloudflare) -> None:
+ response = await async_client.moq.relays.with_raw_response.list(
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ relay = await response.parse()
+ assert_matches_type(AsyncSinglePage[RelayListResponse], relay, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncCloudflare) -> None:
+ async with async_client.moq.relays.with_streaming_response.list(
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ relay = await response.parse()
+ assert_matches_type(AsyncSinglePage[RelayListResponse], relay, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @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.moq.relays.with_raw_response.list(
+ account_id="",
+ )
+
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncCloudflare) -> None:
+ relay = await async_client.moq.relays.delete(
+ relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+ assert_matches_type(object, relay, path=["response"])
+
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncCloudflare) -> None:
+ response = await async_client.moq.relays.with_raw_response.delete(
+ relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ relay = await response.parse()
+ assert_matches_type(object, relay, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncCloudflare) -> None:
+ async with async_client.moq.relays.with_streaming_response.delete(
+ relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ relay = await response.parse()
+ assert_matches_type(object, relay, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @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.moq.relays.with_raw_response.delete(
+ relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890",
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `relay_id` but received ''"):
+ await async_client.moq.relays.with_raw_response.delete(
+ relay_id="",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+
+ @parametrize
+ async def test_method_get(self, async_client: AsyncCloudflare) -> None:
+ relay = await async_client.moq.relays.get(
+ relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+ assert_matches_type(Optional[RelayGetResponse], relay, path=["response"])
+
+ @parametrize
+ async def test_raw_response_get(self, async_client: AsyncCloudflare) -> None:
+ response = await async_client.moq.relays.with_raw_response.get(
+ relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ relay = await response.parse()
+ assert_matches_type(Optional[RelayGetResponse], relay, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_get(self, async_client: AsyncCloudflare) -> None:
+ async with async_client.moq.relays.with_streaming_response.get(
+ relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ relay = await response.parse()
+ assert_matches_type(Optional[RelayGetResponse], relay, 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.moq.relays.with_raw_response.get(
+ relay_id="a1b2c3d4e5f67890a1b2c3d4e5f67890",
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `relay_id` but received ''"):
+ await async_client.moq.relays.with_raw_response.get(
+ relay_id="",
+ account_id="023e105f4ecef8ad9ca31a8372d0c353",
+ )
diff --git a/tests/api_resources/rulesets/test_rules.py b/tests/api_resources/rulesets/test_rules.py
index 0270791a0b1..2b88ce437de 100644
--- a/tests/api_resources/rulesets/test_rules.py
+++ b/tests/api_resources/rulesets/test_rules.py
@@ -1688,6 +1688,20 @@ def test_method_create_with_all_params_overload_17(self, client: Cloudflare) ->
"strip_etags": True,
"strip_last_modified": True,
"strip_set_cookie": True,
+ "vary": {
+ "default": {
+ "action": "normalize",
+ "languages": ["en"],
+ "media_types": ["image/webp"],
+ },
+ "headers": {
+ "accept": {
+ "action": "normalize",
+ "languages": ["en"],
+ "media_types": ["image/webp", "image/png"],
+ }
+ },
+ },
},
description="Configure settings for how the response is cached.",
enabled=True,
@@ -4042,6 +4056,20 @@ def test_method_edit_with_all_params_overload_17(self, client: Cloudflare) -> No
"strip_etags": True,
"strip_last_modified": True,
"strip_set_cookie": True,
+ "vary": {
+ "default": {
+ "action": "normalize",
+ "languages": ["en"],
+ "media_types": ["image/webp"],
+ },
+ "headers": {
+ "accept": {
+ "action": "normalize",
+ "languages": ["en"],
+ "media_types": ["image/webp", "image/png"],
+ }
+ },
+ },
},
description="Configure settings for how the response is cached.",
enabled=True,
@@ -6149,6 +6177,20 @@ async def test_method_create_with_all_params_overload_17(self, async_client: Asy
"strip_etags": True,
"strip_last_modified": True,
"strip_set_cookie": True,
+ "vary": {
+ "default": {
+ "action": "normalize",
+ "languages": ["en"],
+ "media_types": ["image/webp"],
+ },
+ "headers": {
+ "accept": {
+ "action": "normalize",
+ "languages": ["en"],
+ "media_types": ["image/webp", "image/png"],
+ }
+ },
+ },
},
description="Configure settings for how the response is cached.",
enabled=True,
@@ -8503,6 +8545,20 @@ async def test_method_edit_with_all_params_overload_17(self, async_client: Async
"strip_etags": True,
"strip_last_modified": True,
"strip_set_cookie": True,
+ "vary": {
+ "default": {
+ "action": "normalize",
+ "languages": ["en"],
+ "media_types": ["image/webp"],
+ },
+ "headers": {
+ "accept": {
+ "action": "normalize",
+ "languages": ["en"],
+ "media_types": ["image/webp", "image/png"],
+ }
+ },
+ },
},
description="Configure settings for how the response is cached.",
enabled=True,
diff --git a/tests/api_resources/zero_trust/access/test_applications.py b/tests/api_resources/zero_trust/access/test_applications.py
index 8de6e58e2a8..027d2b429d3 100644
--- a/tests/api_resources/zero_trust/access/test_applications.py
+++ b/tests/api_resources/zero_trust/access/test_applications.py
@@ -1084,7 +1084,7 @@ def test_method_create_with_all_params_overload_10(self, client: Cloudflare) ->
},
"exclude": [{"certificate": {}}],
"mfa_config": {
- "allowed_authenticators": ["ssh_piv_key"],
+ "allowed_authenticators": ["piv_key"],
"mfa_disabled": False,
"session_duration": "24h",
},
@@ -2874,7 +2874,7 @@ def test_method_update_with_all_params_overload_10(self, client: Cloudflare) ->
},
"exclude": [{"certificate": {}}],
"mfa_config": {
- "allowed_authenticators": ["ssh_piv_key"],
+ "allowed_authenticators": ["piv_key"],
"mfa_disabled": False,
"session_duration": "24h",
},
@@ -4879,7 +4879,7 @@ async def test_method_create_with_all_params_overload_10(self, async_client: Asy
},
"exclude": [{"certificate": {}}],
"mfa_config": {
- "allowed_authenticators": ["ssh_piv_key"],
+ "allowed_authenticators": ["piv_key"],
"mfa_disabled": False,
"session_duration": "24h",
},
@@ -6669,7 +6669,7 @@ async def test_method_update_with_all_params_overload_10(self, async_client: Asy
},
"exclude": [{"certificate": {}}],
"mfa_config": {
- "allowed_authenticators": ["ssh_piv_key"],
+ "allowed_authenticators": ["piv_key"],
"mfa_disabled": False,
"session_duration": "24h",
},
diff --git a/tests/api_resources/zero_trust/test_organizations.py b/tests/api_resources/zero_trust/test_organizations.py
index acc5cd43afc..a45ee5e8ddc 100644
--- a/tests/api_resources/zero_trust/test_organizations.py
+++ b/tests/api_resources/zero_trust/test_organizations.py
@@ -55,14 +55,14 @@ def test_method_create_with_all_params(self, client: Cloudflare) -> None:
"required_aaguids": "2fc0579f-8113-47ea-b116-bb5a8db9202a",
"session_duration": "24h",
},
- mfa_required_for_all_apps=False,
- mfa_ssh_piv_key_requirements={
+ mfa_piv_key_requirements={
"pin_policy": "always",
"require_fips_device": True,
"ssh_key_size": [256, 2048],
"ssh_key_type": ["ecdsa", "rsa"],
"touch_policy": "always",
},
+ mfa_required_for_all_apps=False,
session_duration="24h",
ui_read_only_toggle_reason="Temporarily turn off the UI read only lock to make a change via the UI",
user_seat_expiration_inactive_time="730h",
@@ -153,14 +153,14 @@ def test_method_update_with_all_params(self, client: Cloudflare) -> None:
"required_aaguids": "2fc0579f-8113-47ea-b116-bb5a8db9202a",
"session_duration": "24h",
},
- mfa_required_for_all_apps=False,
- mfa_ssh_piv_key_requirements={
+ mfa_piv_key_requirements={
"pin_policy": "always",
"require_fips_device": True,
"ssh_key_size": [256, 2048],
"ssh_key_type": ["ecdsa", "rsa"],
"touch_policy": "always",
},
+ mfa_required_for_all_apps=False,
name="Widget Corps Internal Applications",
session_duration="24h",
ui_read_only_toggle_reason="Temporarily turn off the UI read only lock to make a change via the UI",
@@ -369,14 +369,14 @@ async def test_method_create_with_all_params(self, async_client: AsyncCloudflare
"required_aaguids": "2fc0579f-8113-47ea-b116-bb5a8db9202a",
"session_duration": "24h",
},
- mfa_required_for_all_apps=False,
- mfa_ssh_piv_key_requirements={
+ mfa_piv_key_requirements={
"pin_policy": "always",
"require_fips_device": True,
"ssh_key_size": [256, 2048],
"ssh_key_type": ["ecdsa", "rsa"],
"touch_policy": "always",
},
+ mfa_required_for_all_apps=False,
session_duration="24h",
ui_read_only_toggle_reason="Temporarily turn off the UI read only lock to make a change via the UI",
user_seat_expiration_inactive_time="730h",
@@ -467,14 +467,14 @@ async def test_method_update_with_all_params(self, async_client: AsyncCloudflare
"required_aaguids": "2fc0579f-8113-47ea-b116-bb5a8db9202a",
"session_duration": "24h",
},
- mfa_required_for_all_apps=False,
- mfa_ssh_piv_key_requirements={
+ mfa_piv_key_requirements={
"pin_policy": "always",
"require_fips_device": True,
"ssh_key_size": [256, 2048],
"ssh_key_type": ["ecdsa", "rsa"],
"touch_policy": "always",
},
+ mfa_required_for_all_apps=False,
name="Widget Corps Internal Applications",
session_duration="24h",
ui_read_only_toggle_reason="Temporarily turn off the UI read only lock to make a change via the UI",