From 6f8fe45cb794e6283855863693c12143ec2b4871 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 18 Sep 2026 15:48:29 +0000 Subject: [PATCH 1/2] feat(api): manual updates --- .stats.yml | 6 +- api.md | 77 +++- src/prelude_python_sdk/_client.py | 46 +- src/prelude_python_sdk/resources/__init__.py | 14 + .../resources/intel/__init__.py | 33 ++ .../resources/intel/intel.py | 120 +++++ src/prelude_python_sdk/resources/intel/kyc.py | 270 +++++++++++ src/prelude_python_sdk/resources/notify.py | 134 ++++++ .../resources/verification/__init__.py | 33 ++ .../resources/verification/phone/__init__.py | 33 ++ .../resources/verification/phone/history.py | 436 ++++++++++++++++++ .../resources/verification/phone/phone.py | 108 +++++ .../{ => verification}/verification.py | 64 ++- .../verification_management/__init__.py | 33 ++ .../verification_management/sandbox.py | 363 +++++++++++++++ .../verification_management.py | 64 ++- src/prelude_python_sdk/resources/watch.py | 184 +++++++- src/prelude_python_sdk/types/__init__.py | 5 + .../types/intel/__init__.py | 6 + .../types/intel/kyc_match_params.py | 40 ++ .../types/intel/kyc_match_response.py | 84 ++++ .../types/notify_reply_params.py | 34 ++ .../types/notify_reply_response.py | 31 ++ .../types/shared/__init__.py | 4 + .../types/shared/signals.py | 75 +++ src/prelude_python_sdk/types/shared/target.py | 17 + .../types/shared_params/__init__.py | 4 + .../types/shared_params/signals.py | 74 +++ .../types/shared_params/target.py | 17 + .../types/verification/__init__.py | 3 + .../types/verification/phone/__init__.py | 10 + .../verification/phone/history_list_params.py | 85 ++++ .../phone/history_list_response.py | 113 +++++ .../phone/history_retrieve_response.py | 333 +++++++++++++ .../phone/phone_verification_carrier.py | 15 + .../phone/phone_verification_money.py | 17 + .../phone_verification_psd2_transaction.py | 15 + .../types/verification_check_params.py | 19 +- .../types/verification_create_params.py | 85 +--- .../types/verification_management/__init__.py | 8 + .../sandbox_add_phone_number_params.py | 18 + .../sandbox_add_phone_number_response.py | 13 + .../sandbox_delete_phone_number_response.py | 10 + .../sandbox_list_phone_numbers_response.py | 24 + .../types/watch_evaluate_params.py | 48 ++ .../types/watch_evaluate_response.py | 125 +++++ .../types/watch_predict_params.py | 84 +--- .../types/watch_send_events_params.py | 16 +- .../types/watch_send_feedbacks_params.py | 16 +- tests/api_resources/intel/__init__.py | 1 + tests/api_resources/intel/test_kyc.py | 133 ++++++ tests/api_resources/test_notify.py | 97 ++++ tests/api_resources/test_watch.py | 143 ++++++ tests/api_resources/verification/__init__.py | 1 + .../verification/phone/__init__.py | 1 + .../verification/phone/test_history.py | 190 ++++++++ .../verification_management/__init__.py | 1 + .../verification_management/test_sandbox.py | 224 +++++++++ 58 files changed, 4012 insertions(+), 245 deletions(-) create mode 100644 src/prelude_python_sdk/resources/intel/__init__.py create mode 100644 src/prelude_python_sdk/resources/intel/intel.py create mode 100644 src/prelude_python_sdk/resources/intel/kyc.py create mode 100644 src/prelude_python_sdk/resources/verification/__init__.py create mode 100644 src/prelude_python_sdk/resources/verification/phone/__init__.py create mode 100644 src/prelude_python_sdk/resources/verification/phone/history.py create mode 100644 src/prelude_python_sdk/resources/verification/phone/phone.py rename src/prelude_python_sdk/resources/{ => verification}/verification.py (87%) create mode 100644 src/prelude_python_sdk/resources/verification_management/__init__.py create mode 100644 src/prelude_python_sdk/resources/verification_management/sandbox.py rename src/prelude_python_sdk/resources/{ => verification_management}/verification_management.py (90%) create mode 100644 src/prelude_python_sdk/types/intel/__init__.py create mode 100644 src/prelude_python_sdk/types/intel/kyc_match_params.py create mode 100644 src/prelude_python_sdk/types/intel/kyc_match_response.py create mode 100644 src/prelude_python_sdk/types/notify_reply_params.py create mode 100644 src/prelude_python_sdk/types/notify_reply_response.py create mode 100644 src/prelude_python_sdk/types/shared/__init__.py create mode 100644 src/prelude_python_sdk/types/shared/signals.py create mode 100644 src/prelude_python_sdk/types/shared/target.py create mode 100644 src/prelude_python_sdk/types/shared_params/__init__.py create mode 100644 src/prelude_python_sdk/types/shared_params/signals.py create mode 100644 src/prelude_python_sdk/types/shared_params/target.py create mode 100644 src/prelude_python_sdk/types/verification/__init__.py create mode 100644 src/prelude_python_sdk/types/verification/phone/__init__.py create mode 100644 src/prelude_python_sdk/types/verification/phone/history_list_params.py create mode 100644 src/prelude_python_sdk/types/verification/phone/history_list_response.py create mode 100644 src/prelude_python_sdk/types/verification/phone/history_retrieve_response.py create mode 100644 src/prelude_python_sdk/types/verification/phone/phone_verification_carrier.py create mode 100644 src/prelude_python_sdk/types/verification/phone/phone_verification_money.py create mode 100644 src/prelude_python_sdk/types/verification/phone/phone_verification_psd2_transaction.py create mode 100644 src/prelude_python_sdk/types/verification_management/__init__.py create mode 100644 src/prelude_python_sdk/types/verification_management/sandbox_add_phone_number_params.py create mode 100644 src/prelude_python_sdk/types/verification_management/sandbox_add_phone_number_response.py create mode 100644 src/prelude_python_sdk/types/verification_management/sandbox_delete_phone_number_response.py create mode 100644 src/prelude_python_sdk/types/verification_management/sandbox_list_phone_numbers_response.py create mode 100644 src/prelude_python_sdk/types/watch_evaluate_params.py create mode 100644 src/prelude_python_sdk/types/watch_evaluate_response.py create mode 100644 tests/api_resources/intel/__init__.py create mode 100644 tests/api_resources/intel/test_kyc.py create mode 100644 tests/api_resources/verification/__init__.py create mode 100644 tests/api_resources/verification/phone/__init__.py create mode 100644 tests/api_resources/verification/phone/test_history.py create mode 100644 tests/api_resources/verification_management/__init__.py create mode 100644 tests/api_resources/verification_management/test_sandbox.py diff --git a/.stats.yml b/.stats.yml index b57e87a9..cce5f555 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 19 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/prelude/prelude-86fc45f417e6468f6a6087f9e35b264b0a350e31385bc078a2027a1f1abd1d15.yml +configured_endpoints: 27 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/prelude/prelude-33b70f86404b99046db5c61d3142b149dd7dec7eefe5e62ff3b397c7274aa5dc.yml openapi_spec_hash: fb72aba38ec55a3b6b5162c9f5b4228d -config_hash: 3dbe79c14ecd9a153418249f9155d159 +config_hash: 707d65d2f456a2b5ba5b2c565b60128a diff --git a/api.md b/api.md index e6cf4924..8663a685 100644 --- a/api.md +++ b/api.md @@ -1,3 +1,9 @@ +# Shared Types + +```python +from prelude_python_sdk.types import Signals, Target +``` + # Lookup Types: @@ -21,6 +27,7 @@ from prelude_python_sdk.types import ( NotifyListSubscriptionConfigsResponse, NotifyListSubscriptionPhoneNumberEventsResponse, NotifyListSubscriptionPhoneNumbersResponse, + NotifyReplyResponse, NotifySendResponse, NotifySendBatchResponse, ) @@ -33,6 +40,7 @@ Methods: - client.notify.list_subscription_configs(\*\*params) -> NotifyListSubscriptionConfigsResponse - client.notify.list_subscription_phone_number_events(phone_number, \*, config_id, \*\*params) -> NotifyListSubscriptionPhoneNumberEventsResponse - client.notify.list_subscription_phone_numbers(config_id, \*\*params) -> NotifyListSubscriptionPhoneNumbersResponse +- client.notify.reply(\*\*params) -> NotifyReplyResponse - client.notify.send(\*\*params) -> NotifySendResponse - client.notify.send_batch(\*\*params) -> NotifySendBatchResponse @@ -58,8 +66,29 @@ from prelude_python_sdk.types import VerificationCreateResponse, VerificationChe Methods: -- client.verification.create(\*\*params) -> VerificationCreateResponse -- client.verification.check(\*\*params) -> VerificationCheckResponse +- client.verification.create(\*\*params) -> VerificationCreateResponse +- client.verification.check(\*\*params) -> VerificationCheckResponse + +## Phone + +### History + +Types: + +```python +from prelude_python_sdk.types.verification.phone import ( + PhoneVerificationCarrier, + PhoneVerificationMoney, + PhoneVerificationPsd2Transaction, + HistoryRetrieveResponse, + HistoryListResponse, +) +``` + +Methods: + +- client.verification.phone.history.retrieve(id) -> HistoryRetrieveResponse +- client.verification.phone.history.list(\*\*params) -> HistoryListResponse # VerificationManagement @@ -77,11 +106,29 @@ from prelude_python_sdk.types import ( Methods: -- client.verification_management.delete_phone_number(action, \*\*params) -> VerificationManagementDeletePhoneNumberResponse -- client.verification_management.list_phone_numbers(action) -> VerificationManagementListPhoneNumbersResponse -- client.verification_management.list_sender_ids() -> VerificationManagementListSenderIDsResponse -- client.verification_management.set_phone_number(action, \*\*params) -> VerificationManagementSetPhoneNumberResponse -- client.verification_management.submit_sender_id(\*\*params) -> VerificationManagementSubmitSenderIDResponse +- client.verification_management.delete_phone_number(action, \*\*params) -> VerificationManagementDeletePhoneNumberResponse +- client.verification_management.list_phone_numbers(action) -> VerificationManagementListPhoneNumbersResponse +- client.verification_management.list_sender_ids() -> VerificationManagementListSenderIDsResponse +- client.verification_management.set_phone_number(action, \*\*params) -> VerificationManagementSetPhoneNumberResponse +- client.verification_management.submit_sender_id(\*\*params) -> VerificationManagementSubmitSenderIDResponse + +## Sandbox + +Types: + +```python +from prelude_python_sdk.types.verification_management import ( + SandboxAddPhoneNumberResponse, + SandboxDeletePhoneNumberResponse, + SandboxListPhoneNumbersResponse, +) +``` + +Methods: + +- client.verification_management.sandbox.add_phone_number(\*\*params) -> SandboxAddPhoneNumberResponse +- client.verification_management.sandbox.delete_phone_number(phone_number) -> SandboxDeletePhoneNumberResponse +- client.verification_management.sandbox.list_phone_numbers() -> SandboxListPhoneNumbersResponse # Watch @@ -89,6 +136,7 @@ Types: ```python from prelude_python_sdk.types import ( + WatchEvaluateResponse, WatchPredictResponse, WatchSendEventsResponse, WatchSendFeedbacksResponse, @@ -97,6 +145,21 @@ from prelude_python_sdk.types import ( Methods: +- client.watch.evaluate(\*\*params) -> WatchEvaluateResponse - client.watch.predict(\*\*params) -> WatchPredictResponse - client.watch.send_events(\*\*params) -> WatchSendEventsResponse - client.watch.send_feedbacks(\*\*params) -> WatchSendFeedbacksResponse + +# Intel + +## KYC + +Types: + +```python +from prelude_python_sdk.types.intel import KYCMatchResponse +``` + +Methods: + +- client.intel.kyc.match(phone, \*\*params) -> KYCMatchResponse diff --git a/src/prelude_python_sdk/_client.py b/src/prelude_python_sdk/_client.py index 5a0a6dc4..cd9567ef 100644 --- a/src/prelude_python_sdk/_client.py +++ b/src/prelude_python_sdk/_client.py @@ -35,13 +35,17 @@ ) if TYPE_CHECKING: - from .resources import watch, lookup, notify, verification, transactional, verification_management + from .resources import intel, watch, lookup, notify, verification, transactional, verification_management from .resources.watch import WatchResource, AsyncWatchResource from .resources.lookup import LookupResource, AsyncLookupResource from .resources.notify import NotifyResource, AsyncNotifyResource - from .resources.verification import VerificationResource, AsyncVerificationResource + from .resources.intel.intel import IntelResource, AsyncIntelResource from .resources.transactional import TransactionalResource, AsyncTransactionalResource - from .resources.verification_management import VerificationManagementResource, AsyncVerificationManagementResource + from .resources.verification.verification import VerificationResource, AsyncVerificationResource + from .resources.verification_management.verification_management import ( + VerificationManagementResource, + AsyncVerificationManagementResource, + ) __all__ = ["Timeout", "Transport", "ProxiesTypes", "RequestOptions", "Prelude", "AsyncPrelude", "Client", "AsyncClient"] @@ -154,6 +158,12 @@ def watch(self) -> WatchResource: return WatchResource(self) + @cached_property + def intel(self) -> IntelResource: + from .resources.intel import IntelResource + + return IntelResource(self) + @cached_property def with_raw_response(self) -> PreludeWithRawResponse: return PreludeWithRawResponse(self) @@ -375,6 +385,12 @@ def watch(self) -> AsyncWatchResource: return AsyncWatchResource(self) + @cached_property + def intel(self) -> AsyncIntelResource: + from .resources.intel import AsyncIntelResource + + return AsyncIntelResource(self) + @cached_property def with_raw_response(self) -> AsyncPreludeWithRawResponse: return AsyncPreludeWithRawResponse(self) @@ -538,6 +554,12 @@ def watch(self) -> watch.WatchResourceWithRawResponse: return WatchResourceWithRawResponse(self._client.watch) + @cached_property + def intel(self) -> intel.IntelResourceWithRawResponse: + from .resources.intel import IntelResourceWithRawResponse + + return IntelResourceWithRawResponse(self._client.intel) + class AsyncPreludeWithRawResponse: _client: AsyncPrelude @@ -589,6 +611,12 @@ def watch(self) -> watch.AsyncWatchResourceWithRawResponse: return AsyncWatchResourceWithRawResponse(self._client.watch) + @cached_property + def intel(self) -> intel.AsyncIntelResourceWithRawResponse: + from .resources.intel import AsyncIntelResourceWithRawResponse + + return AsyncIntelResourceWithRawResponse(self._client.intel) + class PreludeWithStreamedResponse: _client: Prelude @@ -640,6 +668,12 @@ def watch(self) -> watch.WatchResourceWithStreamingResponse: return WatchResourceWithStreamingResponse(self._client.watch) + @cached_property + def intel(self) -> intel.IntelResourceWithStreamingResponse: + from .resources.intel import IntelResourceWithStreamingResponse + + return IntelResourceWithStreamingResponse(self._client.intel) + class AsyncPreludeWithStreamedResponse: _client: AsyncPrelude @@ -693,6 +727,12 @@ def watch(self) -> watch.AsyncWatchResourceWithStreamingResponse: return AsyncWatchResourceWithStreamingResponse(self._client.watch) + @cached_property + def intel(self) -> intel.AsyncIntelResourceWithStreamingResponse: + from .resources.intel import AsyncIntelResourceWithStreamingResponse + + return AsyncIntelResourceWithStreamingResponse(self._client.intel) + Client = Prelude diff --git a/src/prelude_python_sdk/resources/__init__.py b/src/prelude_python_sdk/resources/__init__.py index 0699078d..662b6c3f 100644 --- a/src/prelude_python_sdk/resources/__init__.py +++ b/src/prelude_python_sdk/resources/__init__.py @@ -1,5 +1,13 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from .intel import ( + IntelResource, + AsyncIntelResource, + IntelResourceWithRawResponse, + AsyncIntelResourceWithRawResponse, + IntelResourceWithStreamingResponse, + AsyncIntelResourceWithStreamingResponse, +) from .watch import ( WatchResource, AsyncWatchResource, @@ -86,4 +94,10 @@ "AsyncWatchResourceWithRawResponse", "WatchResourceWithStreamingResponse", "AsyncWatchResourceWithStreamingResponse", + "IntelResource", + "AsyncIntelResource", + "IntelResourceWithRawResponse", + "AsyncIntelResourceWithRawResponse", + "IntelResourceWithStreamingResponse", + "AsyncIntelResourceWithStreamingResponse", ] diff --git a/src/prelude_python_sdk/resources/intel/__init__.py b/src/prelude_python_sdk/resources/intel/__init__.py new file mode 100644 index 00000000..741e17ac --- /dev/null +++ b/src/prelude_python_sdk/resources/intel/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .kyc import ( + KYCResource, + AsyncKYCResource, + KYCResourceWithRawResponse, + AsyncKYCResourceWithRawResponse, + KYCResourceWithStreamingResponse, + AsyncKYCResourceWithStreamingResponse, +) +from .intel import ( + IntelResource, + AsyncIntelResource, + IntelResourceWithRawResponse, + AsyncIntelResourceWithRawResponse, + IntelResourceWithStreamingResponse, + AsyncIntelResourceWithStreamingResponse, +) + +__all__ = [ + "KYCResource", + "AsyncKYCResource", + "KYCResourceWithRawResponse", + "AsyncKYCResourceWithRawResponse", + "KYCResourceWithStreamingResponse", + "AsyncKYCResourceWithStreamingResponse", + "IntelResource", + "AsyncIntelResource", + "IntelResourceWithRawResponse", + "AsyncIntelResourceWithRawResponse", + "IntelResourceWithStreamingResponse", + "AsyncIntelResourceWithStreamingResponse", +] diff --git a/src/prelude_python_sdk/resources/intel/intel.py b/src/prelude_python_sdk/resources/intel/intel.py new file mode 100644 index 00000000..2d90dbc6 --- /dev/null +++ b/src/prelude_python_sdk/resources/intel/intel.py @@ -0,0 +1,120 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .kyc import ( + KYCResource, + AsyncKYCResource, + KYCResourceWithRawResponse, + AsyncKYCResourceWithRawResponse, + KYCResourceWithStreamingResponse, + AsyncKYCResourceWithStreamingResponse, +) +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource + +__all__ = ["IntelResource", "AsyncIntelResource"] + + +class IntelResource(SyncAPIResource): + @cached_property + def kyc(self) -> KYCResource: + """ + Retrieve detailed information about a phone number including carrier data, line type, and portability status. + """ + return KYCResource(self._client) + + @cached_property + def with_raw_response(self) -> IntelResourceWithRawResponse: + """ + 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/prelude-so/python-sdk#accessing-raw-response-data-eg-headers + """ + return IntelResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> IntelResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/prelude-so/python-sdk#with_streaming_response + """ + return IntelResourceWithStreamingResponse(self) + + +class AsyncIntelResource(AsyncAPIResource): + @cached_property + def kyc(self) -> AsyncKYCResource: + """ + Retrieve detailed information about a phone number including carrier data, line type, and portability status. + """ + return AsyncKYCResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncIntelResourceWithRawResponse: + """ + 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/prelude-so/python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncIntelResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncIntelResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/prelude-so/python-sdk#with_streaming_response + """ + return AsyncIntelResourceWithStreamingResponse(self) + + +class IntelResourceWithRawResponse: + def __init__(self, intel: IntelResource) -> None: + self._intel = intel + + @cached_property + def kyc(self) -> KYCResourceWithRawResponse: + """ + Retrieve detailed information about a phone number including carrier data, line type, and portability status. + """ + return KYCResourceWithRawResponse(self._intel.kyc) + + +class AsyncIntelResourceWithRawResponse: + def __init__(self, intel: AsyncIntelResource) -> None: + self._intel = intel + + @cached_property + def kyc(self) -> AsyncKYCResourceWithRawResponse: + """ + Retrieve detailed information about a phone number including carrier data, line type, and portability status. + """ + return AsyncKYCResourceWithRawResponse(self._intel.kyc) + + +class IntelResourceWithStreamingResponse: + def __init__(self, intel: IntelResource) -> None: + self._intel = intel + + @cached_property + def kyc(self) -> KYCResourceWithStreamingResponse: + """ + Retrieve detailed information about a phone number including carrier data, line type, and portability status. + """ + return KYCResourceWithStreamingResponse(self._intel.kyc) + + +class AsyncIntelResourceWithStreamingResponse: + def __init__(self, intel: AsyncIntelResource) -> None: + self._intel = intel + + @cached_property + def kyc(self) -> AsyncKYCResourceWithStreamingResponse: + """ + Retrieve detailed information about a phone number including carrier data, line type, and portability status. + """ + return AsyncKYCResourceWithStreamingResponse(self._intel.kyc) diff --git a/src/prelude_python_sdk/resources/intel/kyc.py b/src/prelude_python_sdk/resources/intel/kyc.py new file mode 100644 index 00000000..c2caa9ca --- /dev/null +++ b/src/prelude_python_sdk/resources/intel/kyc.py @@ -0,0 +1,270 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import date + +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 ...types.intel import kyc_match_params +from ..._base_client import make_request_options +from ...types.intel.kyc_match_response import KYCMatchResponse + +__all__ = ["KYCResource", "AsyncKYCResource"] + + +class KYCResource(SyncAPIResource): + """ + Retrieve detailed information about a phone number including carrier data, line type, and portability status. + """ + + @cached_property + def with_raw_response(self) -> KYCResourceWithRawResponse: + """ + 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/prelude-so/python-sdk#accessing-raw-response-data-eg-headers + """ + return KYCResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> KYCResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/prelude-so/python-sdk#with_streaming_response + """ + return KYCResourceWithStreamingResponse(self) + + def match( + self, + phone: str, + *, + address: str | Omit = omit, + birthdate: Union[str, date] | Omit = omit, + country: str | Omit = omit, + email: str | Omit = omit, + family_name: str | Omit = omit, + given_name: str | Omit = omit, + locality: str | Omit = omit, + postal_code: str | Omit = omit, + region: 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, + ) -> KYCMatchResponse: + """ + Verify identity attributes against the subscriber record held by the end-user's + mobile operator. Send a phone number along with the attributes to check; Prelude + resolves the operator internally and returns a per-attribute match. Currently + available for France only (Orange, SFR, Bouygues) and must be enabled for your + account. + + Args: + phone: An E.164 formatted phone number whose subscriber identity to match against. + + address: The street address. + + birthdate: The date of birth in ISO 8601 (`YYYY-MM-DD`) format. Compared exactly. + + country: The ISO 3166-1 alpha-2 country code. Compared exactly. + + email: The email address. + + family_name: The end-user's family (last) name. + + given_name: The end-user's given (first) name. + + locality: The locality (city). + + postal_code: The postal code. Compared exactly. + + region: The region, state, or province. + + 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 phone: + raise ValueError(f"Expected a non-empty value for `phone` but received {phone!r}") + return self._post( + path_template("/v2/intel/kyc/match/{phone}", phone=phone), + body=maybe_transform( + { + "address": address, + "birthdate": birthdate, + "country": country, + "email": email, + "family_name": family_name, + "given_name": given_name, + "locality": locality, + "postal_code": postal_code, + "region": region, + }, + kyc_match_params.KYCMatchParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KYCMatchResponse, + ) + + +class AsyncKYCResource(AsyncAPIResource): + """ + Retrieve detailed information about a phone number including carrier data, line type, and portability status. + """ + + @cached_property + def with_raw_response(self) -> AsyncKYCResourceWithRawResponse: + """ + 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/prelude-so/python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncKYCResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncKYCResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/prelude-so/python-sdk#with_streaming_response + """ + return AsyncKYCResourceWithStreamingResponse(self) + + async def match( + self, + phone: str, + *, + address: str | Omit = omit, + birthdate: Union[str, date] | Omit = omit, + country: str | Omit = omit, + email: str | Omit = omit, + family_name: str | Omit = omit, + given_name: str | Omit = omit, + locality: str | Omit = omit, + postal_code: str | Omit = omit, + region: 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, + ) -> KYCMatchResponse: + """ + Verify identity attributes against the subscriber record held by the end-user's + mobile operator. Send a phone number along with the attributes to check; Prelude + resolves the operator internally and returns a per-attribute match. Currently + available for France only (Orange, SFR, Bouygues) and must be enabled for your + account. + + Args: + phone: An E.164 formatted phone number whose subscriber identity to match against. + + address: The street address. + + birthdate: The date of birth in ISO 8601 (`YYYY-MM-DD`) format. Compared exactly. + + country: The ISO 3166-1 alpha-2 country code. Compared exactly. + + email: The email address. + + family_name: The end-user's family (last) name. + + given_name: The end-user's given (first) name. + + locality: The locality (city). + + postal_code: The postal code. Compared exactly. + + region: The region, state, or province. + + 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 phone: + raise ValueError(f"Expected a non-empty value for `phone` but received {phone!r}") + return await self._post( + path_template("/v2/intel/kyc/match/{phone}", phone=phone), + body=await async_maybe_transform( + { + "address": address, + "birthdate": birthdate, + "country": country, + "email": email, + "family_name": family_name, + "given_name": given_name, + "locality": locality, + "postal_code": postal_code, + "region": region, + }, + kyc_match_params.KYCMatchParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=KYCMatchResponse, + ) + + +class KYCResourceWithRawResponse: + def __init__(self, kyc: KYCResource) -> None: + self._kyc = kyc + + self.match = to_raw_response_wrapper( + kyc.match, + ) + + +class AsyncKYCResourceWithRawResponse: + def __init__(self, kyc: AsyncKYCResource) -> None: + self._kyc = kyc + + self.match = async_to_raw_response_wrapper( + kyc.match, + ) + + +class KYCResourceWithStreamingResponse: + def __init__(self, kyc: KYCResource) -> None: + self._kyc = kyc + + self.match = to_streamed_response_wrapper( + kyc.match, + ) + + +class AsyncKYCResourceWithStreamingResponse: + def __init__(self, kyc: AsyncKYCResource) -> None: + self._kyc = kyc + + self.match = async_to_streamed_response_wrapper( + kyc.match, + ) diff --git a/src/prelude_python_sdk/resources/notify.py b/src/prelude_python_sdk/resources/notify.py index 5cf6e38a..23e04473 100644 --- a/src/prelude_python_sdk/resources/notify.py +++ b/src/prelude_python_sdk/resources/notify.py @@ -10,6 +10,7 @@ from ..types import ( notify_send_params, + notify_reply_params, notify_send_batch_params, notify_list_subscription_configs_params, notify_list_subscription_phone_numbers_params, @@ -27,6 +28,7 @@ ) from .._base_client import make_request_options from ..types.notify_send_response import NotifySendResponse +from ..types.notify_reply_response import NotifyReplyResponse from ..types.notify_send_batch_response import NotifySendBatchResponse from ..types.notify_get_subscription_config_response import NotifyGetSubscriptionConfigResponse from ..types.notify_list_subscription_configs_response import NotifyListSubscriptionConfigsResponse @@ -310,6 +312,66 @@ def list_subscription_phone_numbers( cast_to=NotifyListSubscriptionPhoneNumbersResponse, ) + def reply( + self, + *, + reply_to: str, + text: str, + to: str, + callback_url: str | Omit = omit, + correlation_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> NotifyReplyResponse: + """ + Send a free-form text reply to an inbound WhatsApp message within the 24-hour + conversation window. See + [WhatsApp 2-Way Messaging](/notify/v2/documentation/whatsapp) for details. + + Args: + reply_to: The inbound message ID (prefixed with `im_`) to reply to. This ID is provided in + the `inbound.message.received` webhook event. + + text: The reply message body sent as a free-form WhatsApp text. + + to: The recipient's phone number in E.164 format. Must match the phone number that + sent the original inbound message. + + callback_url: The URL where webhooks will be sent for delivery events of this reply. + + correlation_id: A user-defined identifier to correlate this reply with your internal systems. It + is returned in the response and any webhook events that refer to this message. + + 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 + """ + return self._post( + "/v2/notify/reply", + body=maybe_transform( + { + "reply_to": reply_to, + "text": text, + "to": to, + "callback_url": callback_url, + "correlation_id": correlation_id, + }, + notify_reply_params.NotifyReplyParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NotifyReplyResponse, + ) + def send( self, *, @@ -786,6 +848,66 @@ async def list_subscription_phone_numbers( cast_to=NotifyListSubscriptionPhoneNumbersResponse, ) + async def reply( + self, + *, + reply_to: str, + text: str, + to: str, + callback_url: str | Omit = omit, + correlation_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> NotifyReplyResponse: + """ + Send a free-form text reply to an inbound WhatsApp message within the 24-hour + conversation window. See + [WhatsApp 2-Way Messaging](/notify/v2/documentation/whatsapp) for details. + + Args: + reply_to: The inbound message ID (prefixed with `im_`) to reply to. This ID is provided in + the `inbound.message.received` webhook event. + + text: The reply message body sent as a free-form WhatsApp text. + + to: The recipient's phone number in E.164 format. Must match the phone number that + sent the original inbound message. + + callback_url: The URL where webhooks will be sent for delivery events of this reply. + + correlation_id: A user-defined identifier to correlate this reply with your internal systems. It + is returned in the response and any webhook events that refer to this message. + + 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 + """ + return await self._post( + "/v2/notify/reply", + body=await async_maybe_transform( + { + "reply_to": reply_to, + "text": text, + "to": to, + "callback_url": callback_url, + "correlation_id": correlation_id, + }, + notify_reply_params.NotifyReplyParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NotifyReplyResponse, + ) + async def send( self, *, @@ -1010,6 +1132,9 @@ def __init__(self, notify: NotifyResource) -> None: self.list_subscription_phone_numbers = to_raw_response_wrapper( notify.list_subscription_phone_numbers, ) + self.reply = to_raw_response_wrapper( + notify.reply, + ) self.send = to_raw_response_wrapper( notify.send, ) @@ -1037,6 +1162,9 @@ def __init__(self, notify: AsyncNotifyResource) -> None: self.list_subscription_phone_numbers = async_to_raw_response_wrapper( notify.list_subscription_phone_numbers, ) + self.reply = async_to_raw_response_wrapper( + notify.reply, + ) self.send = async_to_raw_response_wrapper( notify.send, ) @@ -1064,6 +1192,9 @@ def __init__(self, notify: NotifyResource) -> None: self.list_subscription_phone_numbers = to_streamed_response_wrapper( notify.list_subscription_phone_numbers, ) + self.reply = to_streamed_response_wrapper( + notify.reply, + ) self.send = to_streamed_response_wrapper( notify.send, ) @@ -1091,6 +1222,9 @@ def __init__(self, notify: AsyncNotifyResource) -> None: self.list_subscription_phone_numbers = async_to_streamed_response_wrapper( notify.list_subscription_phone_numbers, ) + self.reply = async_to_streamed_response_wrapper( + notify.reply, + ) self.send = async_to_streamed_response_wrapper( notify.send, ) diff --git a/src/prelude_python_sdk/resources/verification/__init__.py b/src/prelude_python_sdk/resources/verification/__init__.py new file mode 100644 index 00000000..81689f4e --- /dev/null +++ b/src/prelude_python_sdk/resources/verification/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .phone import ( + PhoneResource, + AsyncPhoneResource, + PhoneResourceWithRawResponse, + AsyncPhoneResourceWithRawResponse, + PhoneResourceWithStreamingResponse, + AsyncPhoneResourceWithStreamingResponse, +) +from .verification import ( + VerificationResource, + AsyncVerificationResource, + VerificationResourceWithRawResponse, + AsyncVerificationResourceWithRawResponse, + VerificationResourceWithStreamingResponse, + AsyncVerificationResourceWithStreamingResponse, +) + +__all__ = [ + "PhoneResource", + "AsyncPhoneResource", + "PhoneResourceWithRawResponse", + "AsyncPhoneResourceWithRawResponse", + "PhoneResourceWithStreamingResponse", + "AsyncPhoneResourceWithStreamingResponse", + "VerificationResource", + "AsyncVerificationResource", + "VerificationResourceWithRawResponse", + "AsyncVerificationResourceWithRawResponse", + "VerificationResourceWithStreamingResponse", + "AsyncVerificationResourceWithStreamingResponse", +] diff --git a/src/prelude_python_sdk/resources/verification/phone/__init__.py b/src/prelude_python_sdk/resources/verification/phone/__init__.py new file mode 100644 index 00000000..d6d3cba6 --- /dev/null +++ b/src/prelude_python_sdk/resources/verification/phone/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .phone import ( + PhoneResource, + AsyncPhoneResource, + PhoneResourceWithRawResponse, + AsyncPhoneResourceWithRawResponse, + PhoneResourceWithStreamingResponse, + AsyncPhoneResourceWithStreamingResponse, +) +from .history import ( + HistoryResource, + AsyncHistoryResource, + HistoryResourceWithRawResponse, + AsyncHistoryResourceWithRawResponse, + HistoryResourceWithStreamingResponse, + AsyncHistoryResourceWithStreamingResponse, +) + +__all__ = [ + "HistoryResource", + "AsyncHistoryResource", + "HistoryResourceWithRawResponse", + "AsyncHistoryResourceWithRawResponse", + "HistoryResourceWithStreamingResponse", + "AsyncHistoryResourceWithStreamingResponse", + "PhoneResource", + "AsyncPhoneResource", + "PhoneResourceWithRawResponse", + "AsyncPhoneResourceWithRawResponse", + "PhoneResourceWithStreamingResponse", + "AsyncPhoneResourceWithStreamingResponse", +] diff --git a/src/prelude_python_sdk/resources/verification/phone/history.py b/src/prelude_python_sdk/resources/verification/phone/history.py new file mode 100644 index 00000000..ea8dc483 --- /dev/null +++ b/src/prelude_python_sdk/resources/verification/phone/history.py @@ -0,0 +1,436 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union +from datetime import datetime +from typing_extensions import Literal + +import httpx + +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import path_template, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options +from ....types.verification.phone import history_list_params +from ....types.verification.phone.history_list_response import HistoryListResponse +from ....types.verification.phone.history_retrieve_response import HistoryRetrieveResponse + +__all__ = ["HistoryResource", "AsyncHistoryResource"] + + +class HistoryResource(SyncAPIResource): + """Verify phone numbers.""" + + @cached_property + def with_raw_response(self) -> HistoryResourceWithRawResponse: + """ + 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/prelude-so/python-sdk#accessing-raw-response-data-eg-headers + """ + return HistoryResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> HistoryResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/prelude-so/python-sdk#with_streaming_response + """ + return HistoryResourceWithStreamingResponse(self) + + def retrieve( + self, + 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, + ) -> HistoryRetrieveResponse: + """ + Retrieve everything Prelude recorded for one phone verification: its outcome and + the device, network and anti-fraud context it was created in, the chronological + timeline of every message attempt and code check, and the anti-fraud signals you + forwarded. + + The identifier is the `id` returned by + [Create or retry a verification](/verify/v2/api-reference/create-or-retry-a-verification) + or the `verification_id` of the verification webhooks. Both `lifecycle` and + `signals` are optional: a verification can resolve with its top-level fields + alone. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + path_template("/v2/verification/phone/history/{id}", id=id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=HistoryRetrieveResponse, + ) + + def list( + self, + *, + channels: List[Literal["sms", "rcs", "whatsapp", "viber", "zalo", "telegram", "voice", "silent"]] | Omit = omit, + cursor: str | Omit = omit, + device_platform: Literal["android", "ios", "ipados", "tvos", "web"] | Omit = omit, + from_: Union[str, datetime] | Omit = omit, + limit: int | Omit = omit, + max_attempts: int | Omit = omit, + min_attempts: int | Omit = omit, + phone_number: str | Omit = omit, + region: str | Omit = omit, + status: Literal[ + "converted", + "not_converted", + "pending_check", + "sent", + "challenged", + "suspected_fraud", + "in_blocklist", + "invalid_line", + "invalid_number", + "rate_limited", + "expired_signals", + "shadowed", + ] + | Omit = omit, + template_id: str | Omit = omit, + to: Union[str, datetime] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> HistoryListResponse: + """ + List your phone verifications, most recent first, one entry per verification + with its outcome, channels, attempts and cost. Every filter is optional and they + combine with AND. + + Use it to find every verification a phone number went through from your support + tooling, then + [Get a phone verification](/verify/v2/api-reference/history/get-a-phone-verification) + for the full timeline of one of them. A cursor is bound to the filters that + produced it: pass `next_cursor` back with the exact same query parameters. + + Args: + channels: Only verifications that could use one of these channels. Repeat the parameter + for several values. + + cursor: Pagination cursor from the previous response. + + device_platform: Only verifications created from this device platform. + + from_: Only verifications created at or after this RFC 3339 timestamp. Goes with `to`, + at most 6 months apart. Without them the whole history is searched. + + limit: Maximum number of verifications to return per page. + + max_attempts: Only verifications that sent at most this many messages. `0` keeps the + verifications that never sent one. + + min_attempts: Only verifications that sent at least this many messages. + + phone_number: Only verifications targeting this E.164 phone number. The leading `+` may be + omitted. + + region: Only verifications of phone numbers from this region, as an ISO 3166-1 alpha-2 + code. + + status: Only verifications in this status. `pending_check` cannot be filtered on. + + template_id: Only verifications sent with this template, as returned in `template_id` by + [Get a phone verification](/verify/v2/api-reference/history/get-a-phone-verification). + Built-in templates (`prelude:*`) cannot be filtered on. + + to: Only verifications created at or before this RFC 3339 timestamp. Goes with + `from`. + + 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 + """ + return self._get( + "/v2/verification/phone/history", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "channels": channels, + "cursor": cursor, + "device_platform": device_platform, + "from_": from_, + "limit": limit, + "max_attempts": max_attempts, + "min_attempts": min_attempts, + "phone_number": phone_number, + "region": region, + "status": status, + "template_id": template_id, + "to": to, + }, + history_list_params.HistoryListParams, + ), + ), + cast_to=HistoryListResponse, + ) + + +class AsyncHistoryResource(AsyncAPIResource): + """Verify phone numbers.""" + + @cached_property + def with_raw_response(self) -> AsyncHistoryResourceWithRawResponse: + """ + 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/prelude-so/python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncHistoryResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncHistoryResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/prelude-so/python-sdk#with_streaming_response + """ + return AsyncHistoryResourceWithStreamingResponse(self) + + async def retrieve( + self, + 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, + ) -> HistoryRetrieveResponse: + """ + Retrieve everything Prelude recorded for one phone verification: its outcome and + the device, network and anti-fraud context it was created in, the chronological + timeline of every message attempt and code check, and the anti-fraud signals you + forwarded. + + The identifier is the `id` returned by + [Create or retry a verification](/verify/v2/api-reference/create-or-retry-a-verification) + or the `verification_id` of the verification webhooks. Both `lifecycle` and + `signals` are optional: a verification can resolve with its top-level fields + alone. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + path_template("/v2/verification/phone/history/{id}", id=id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=HistoryRetrieveResponse, + ) + + async def list( + self, + *, + channels: List[Literal["sms", "rcs", "whatsapp", "viber", "zalo", "telegram", "voice", "silent"]] | Omit = omit, + cursor: str | Omit = omit, + device_platform: Literal["android", "ios", "ipados", "tvos", "web"] | Omit = omit, + from_: Union[str, datetime] | Omit = omit, + limit: int | Omit = omit, + max_attempts: int | Omit = omit, + min_attempts: int | Omit = omit, + phone_number: str | Omit = omit, + region: str | Omit = omit, + status: Literal[ + "converted", + "not_converted", + "pending_check", + "sent", + "challenged", + "suspected_fraud", + "in_blocklist", + "invalid_line", + "invalid_number", + "rate_limited", + "expired_signals", + "shadowed", + ] + | Omit = omit, + template_id: str | Omit = omit, + to: Union[str, datetime] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> HistoryListResponse: + """ + List your phone verifications, most recent first, one entry per verification + with its outcome, channels, attempts and cost. Every filter is optional and they + combine with AND. + + Use it to find every verification a phone number went through from your support + tooling, then + [Get a phone verification](/verify/v2/api-reference/history/get-a-phone-verification) + for the full timeline of one of them. A cursor is bound to the filters that + produced it: pass `next_cursor` back with the exact same query parameters. + + Args: + channels: Only verifications that could use one of these channels. Repeat the parameter + for several values. + + cursor: Pagination cursor from the previous response. + + device_platform: Only verifications created from this device platform. + + from_: Only verifications created at or after this RFC 3339 timestamp. Goes with `to`, + at most 6 months apart. Without them the whole history is searched. + + limit: Maximum number of verifications to return per page. + + max_attempts: Only verifications that sent at most this many messages. `0` keeps the + verifications that never sent one. + + min_attempts: Only verifications that sent at least this many messages. + + phone_number: Only verifications targeting this E.164 phone number. The leading `+` may be + omitted. + + region: Only verifications of phone numbers from this region, as an ISO 3166-1 alpha-2 + code. + + status: Only verifications in this status. `pending_check` cannot be filtered on. + + template_id: Only verifications sent with this template, as returned in `template_id` by + [Get a phone verification](/verify/v2/api-reference/history/get-a-phone-verification). + Built-in templates (`prelude:*`) cannot be filtered on. + + to: Only verifications created at or before this RFC 3339 timestamp. Goes with + `from`. + + 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 + """ + return await self._get( + "/v2/verification/phone/history", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "channels": channels, + "cursor": cursor, + "device_platform": device_platform, + "from_": from_, + "limit": limit, + "max_attempts": max_attempts, + "min_attempts": min_attempts, + "phone_number": phone_number, + "region": region, + "status": status, + "template_id": template_id, + "to": to, + }, + history_list_params.HistoryListParams, + ), + ), + cast_to=HistoryListResponse, + ) + + +class HistoryResourceWithRawResponse: + def __init__(self, history: HistoryResource) -> None: + self._history = history + + self.retrieve = to_raw_response_wrapper( + history.retrieve, + ) + self.list = to_raw_response_wrapper( + history.list, + ) + + +class AsyncHistoryResourceWithRawResponse: + def __init__(self, history: AsyncHistoryResource) -> None: + self._history = history + + self.retrieve = async_to_raw_response_wrapper( + history.retrieve, + ) + self.list = async_to_raw_response_wrapper( + history.list, + ) + + +class HistoryResourceWithStreamingResponse: + def __init__(self, history: HistoryResource) -> None: + self._history = history + + self.retrieve = to_streamed_response_wrapper( + history.retrieve, + ) + self.list = to_streamed_response_wrapper( + history.list, + ) + + +class AsyncHistoryResourceWithStreamingResponse: + def __init__(self, history: AsyncHistoryResource) -> None: + self._history = history + + self.retrieve = async_to_streamed_response_wrapper( + history.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + history.list, + ) diff --git a/src/prelude_python_sdk/resources/verification/phone/phone.py b/src/prelude_python_sdk/resources/verification/phone/phone.py new file mode 100644 index 00000000..bad92e9f --- /dev/null +++ b/src/prelude_python_sdk/resources/verification/phone/phone.py @@ -0,0 +1,108 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .history import ( + HistoryResource, + AsyncHistoryResource, + HistoryResourceWithRawResponse, + AsyncHistoryResourceWithRawResponse, + HistoryResourceWithStreamingResponse, + AsyncHistoryResourceWithStreamingResponse, +) +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource + +__all__ = ["PhoneResource", "AsyncPhoneResource"] + + +class PhoneResource(SyncAPIResource): + @cached_property + def history(self) -> HistoryResource: + """Verify phone numbers.""" + return HistoryResource(self._client) + + @cached_property + def with_raw_response(self) -> PhoneResourceWithRawResponse: + """ + 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/prelude-so/python-sdk#accessing-raw-response-data-eg-headers + """ + return PhoneResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> PhoneResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/prelude-so/python-sdk#with_streaming_response + """ + return PhoneResourceWithStreamingResponse(self) + + +class AsyncPhoneResource(AsyncAPIResource): + @cached_property + def history(self) -> AsyncHistoryResource: + """Verify phone numbers.""" + return AsyncHistoryResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncPhoneResourceWithRawResponse: + """ + 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/prelude-so/python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncPhoneResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncPhoneResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/prelude-so/python-sdk#with_streaming_response + """ + return AsyncPhoneResourceWithStreamingResponse(self) + + +class PhoneResourceWithRawResponse: + def __init__(self, phone: PhoneResource) -> None: + self._phone = phone + + @cached_property + def history(self) -> HistoryResourceWithRawResponse: + """Verify phone numbers.""" + return HistoryResourceWithRawResponse(self._phone.history) + + +class AsyncPhoneResourceWithRawResponse: + def __init__(self, phone: AsyncPhoneResource) -> None: + self._phone = phone + + @cached_property + def history(self) -> AsyncHistoryResourceWithRawResponse: + """Verify phone numbers.""" + return AsyncHistoryResourceWithRawResponse(self._phone.history) + + +class PhoneResourceWithStreamingResponse: + def __init__(self, phone: PhoneResource) -> None: + self._phone = phone + + @cached_property + def history(self) -> HistoryResourceWithStreamingResponse: + """Verify phone numbers.""" + return HistoryResourceWithStreamingResponse(self._phone.history) + + +class AsyncPhoneResourceWithStreamingResponse: + def __init__(self, phone: AsyncPhoneResource) -> None: + self._phone = phone + + @cached_property + def history(self) -> AsyncHistoryResourceWithStreamingResponse: + """Verify phone numbers.""" + return AsyncHistoryResourceWithStreamingResponse(self._phone.history) diff --git a/src/prelude_python_sdk/resources/verification.py b/src/prelude_python_sdk/resources/verification/verification.py similarity index 87% rename from src/prelude_python_sdk/resources/verification.py rename to src/prelude_python_sdk/resources/verification/verification.py index 80cb8abb..d836b088 100644 --- a/src/prelude_python_sdk/resources/verification.py +++ b/src/prelude_python_sdk/resources/verification/verification.py @@ -4,20 +4,30 @@ import httpx -from ..types import verification_check_params, verification_create_params -from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( +from ...types import verification_check_params, verification_create_params +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( to_raw_response_wrapper, to_streamed_response_wrapper, async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) -from .._base_client import make_request_options -from ..types.verification_check_response import VerificationCheckResponse -from ..types.verification_create_response import VerificationCreateResponse +from .phone.phone import ( + PhoneResource, + AsyncPhoneResource, + PhoneResourceWithRawResponse, + AsyncPhoneResourceWithRawResponse, + PhoneResourceWithStreamingResponse, + AsyncPhoneResourceWithStreamingResponse, +) +from ..._base_client import make_request_options +from ...types.shared_params.target import Target +from ...types.shared_params.signals import Signals +from ...types.verification_check_response import VerificationCheckResponse +from ...types.verification_create_response import VerificationCreateResponse __all__ = ["VerificationResource", "AsyncVerificationResource"] @@ -25,6 +35,10 @@ class VerificationResource(SyncAPIResource): """Verify phone numbers.""" + @cached_property + def phone(self) -> PhoneResource: + return PhoneResource(self._client) + @cached_property def with_raw_response(self) -> VerificationResourceWithRawResponse: """ @@ -47,11 +61,11 @@ def with_streaming_response(self) -> VerificationResourceWithStreamingResponse: def create( self, *, - target: verification_create_params.Target, + target: Target, dispatch_id: str | Omit = omit, metadata: verification_create_params.Metadata | Omit = omit, options: verification_create_params.Options | Omit = omit, - signals: verification_create_params.Signals | Omit = omit, + signals: Signals | 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, @@ -109,7 +123,7 @@ def check( self, *, code: str, - target: verification_check_params.Target, + target: Target, psd2: verification_check_params.Psd2 | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -160,6 +174,10 @@ def check( class AsyncVerificationResource(AsyncAPIResource): """Verify phone numbers.""" + @cached_property + def phone(self) -> AsyncPhoneResource: + return AsyncPhoneResource(self._client) + @cached_property def with_raw_response(self) -> AsyncVerificationResourceWithRawResponse: """ @@ -182,11 +200,11 @@ def with_streaming_response(self) -> AsyncVerificationResourceWithStreamingRespo async def create( self, *, - target: verification_create_params.Target, + target: Target, dispatch_id: str | Omit = omit, metadata: verification_create_params.Metadata | Omit = omit, options: verification_create_params.Options | Omit = omit, - signals: verification_create_params.Signals | Omit = omit, + signals: Signals | 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, @@ -244,7 +262,7 @@ async def check( self, *, code: str, - target: verification_check_params.Target, + target: Target, psd2: verification_check_params.Psd2 | 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. @@ -303,6 +321,10 @@ def __init__(self, verification: VerificationResource) -> None: verification.check, ) + @cached_property + def phone(self) -> PhoneResourceWithRawResponse: + return PhoneResourceWithRawResponse(self._verification.phone) + class AsyncVerificationResourceWithRawResponse: def __init__(self, verification: AsyncVerificationResource) -> None: @@ -315,6 +337,10 @@ def __init__(self, verification: AsyncVerificationResource) -> None: verification.check, ) + @cached_property + def phone(self) -> AsyncPhoneResourceWithRawResponse: + return AsyncPhoneResourceWithRawResponse(self._verification.phone) + class VerificationResourceWithStreamingResponse: def __init__(self, verification: VerificationResource) -> None: @@ -327,6 +353,10 @@ def __init__(self, verification: VerificationResource) -> None: verification.check, ) + @cached_property + def phone(self) -> PhoneResourceWithStreamingResponse: + return PhoneResourceWithStreamingResponse(self._verification.phone) + class AsyncVerificationResourceWithStreamingResponse: def __init__(self, verification: AsyncVerificationResource) -> None: @@ -338,3 +368,7 @@ def __init__(self, verification: AsyncVerificationResource) -> None: self.check = async_to_streamed_response_wrapper( verification.check, ) + + @cached_property + def phone(self) -> AsyncPhoneResourceWithStreamingResponse: + return AsyncPhoneResourceWithStreamingResponse(self._verification.phone) diff --git a/src/prelude_python_sdk/resources/verification_management/__init__.py b/src/prelude_python_sdk/resources/verification_management/__init__.py new file mode 100644 index 00000000..264240bc --- /dev/null +++ b/src/prelude_python_sdk/resources/verification_management/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .sandbox import ( + SandboxResource, + AsyncSandboxResource, + SandboxResourceWithRawResponse, + AsyncSandboxResourceWithRawResponse, + SandboxResourceWithStreamingResponse, + AsyncSandboxResourceWithStreamingResponse, +) +from .verification_management import ( + VerificationManagementResource, + AsyncVerificationManagementResource, + VerificationManagementResourceWithRawResponse, + AsyncVerificationManagementResourceWithRawResponse, + VerificationManagementResourceWithStreamingResponse, + AsyncVerificationManagementResourceWithStreamingResponse, +) + +__all__ = [ + "SandboxResource", + "AsyncSandboxResource", + "SandboxResourceWithRawResponse", + "AsyncSandboxResourceWithRawResponse", + "SandboxResourceWithStreamingResponse", + "AsyncSandboxResourceWithStreamingResponse", + "VerificationManagementResource", + "AsyncVerificationManagementResource", + "VerificationManagementResourceWithRawResponse", + "AsyncVerificationManagementResourceWithRawResponse", + "VerificationManagementResourceWithStreamingResponse", + "AsyncVerificationManagementResourceWithStreamingResponse", +] diff --git a/src/prelude_python_sdk/resources/verification_management/sandbox.py b/src/prelude_python_sdk/resources/verification_management/sandbox.py new file mode 100644 index 00000000..0a787d53 --- /dev/null +++ b/src/prelude_python_sdk/resources/verification_management/sandbox.py @@ -0,0 +1,363 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +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 ..._base_client import make_request_options +from ...types.verification_management import sandbox_add_phone_number_params +from ...types.verification_management.sandbox_add_phone_number_response import SandboxAddPhoneNumberResponse +from ...types.verification_management.sandbox_list_phone_numbers_response import SandboxListPhoneNumbersResponse +from ...types.verification_management.sandbox_delete_phone_number_response import SandboxDeletePhoneNumberResponse + +__all__ = ["SandboxResource", "AsyncSandboxResource"] + + +class SandboxResource(SyncAPIResource): + """Verify phone numbers.""" + + @cached_property + def with_raw_response(self) -> SandboxResourceWithRawResponse: + """ + 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/prelude-so/python-sdk#accessing-raw-response-data-eg-headers + """ + return SandboxResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SandboxResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/prelude-so/python-sdk#with_streaming_response + """ + return SandboxResourceWithStreamingResponse(self) + + def add_phone_number( + self, + *, + attempt_code: str, + phone_number: 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, + ) -> SandboxAddPhoneNumberResponse: + """ + Register a phone number as a sandbox number and associate it with a fixed + attempt code. Subsequent verification attempts against this number will not + trigger a real SMS/call and will validate against the configured attempt code. + + This operation is idempotent - re-adding the same phone number will overwrite + the existing attempt code. + + In order to get access to this endpoint, contact our support team. + + Args: + attempt_code: The fixed attempt code that will validate verification attempts for this phone + number. + + phone_number: An E.164 formatted phone number to add to the sandbox list. + + 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 + """ + return self._put( + "/v2/verification/management/phone-numbers/sandbox", + body=maybe_transform( + { + "attempt_code": attempt_code, + "phone_number": phone_number, + }, + sandbox_add_phone_number_params.SandboxAddPhoneNumberParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SandboxAddPhoneNumberResponse, + ) + + def delete_phone_number( + self, + phone_number: 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, + ) -> SandboxDeletePhoneNumberResponse: + """ + Remove a phone number from the sandbox list. + + This operation is idempotent - deleting a phone number that is not in the + sandbox list will succeed without making any changes. + + In order to get access to this endpoint, contact our support team. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not phone_number: + raise ValueError(f"Expected a non-empty value for `phone_number` but received {phone_number!r}") + return self._delete( + path_template( + "/v2/verification/management/phone-numbers/sandbox/{phone_number}", phone_number=phone_number + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SandboxDeletePhoneNumberResponse, + ) + + def list_phone_numbers( + self, + *, + # 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, + ) -> SandboxListPhoneNumbersResponse: + """Retrieve the list of sandbox phone numbers for the account. + + Sandbox numbers are + test numbers that bypass the real verification flow and return a fixed attempt + code. + + In order to get access to this endpoint, contact our support team. + """ + return self._get( + "/v2/verification/management/phone-numbers/sandbox", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SandboxListPhoneNumbersResponse, + ) + + +class AsyncSandboxResource(AsyncAPIResource): + """Verify phone numbers.""" + + @cached_property + def with_raw_response(self) -> AsyncSandboxResourceWithRawResponse: + """ + 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/prelude-so/python-sdk#accessing-raw-response-data-eg-headers + """ + return AsyncSandboxResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSandboxResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/prelude-so/python-sdk#with_streaming_response + """ + return AsyncSandboxResourceWithStreamingResponse(self) + + async def add_phone_number( + self, + *, + attempt_code: str, + phone_number: 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, + ) -> SandboxAddPhoneNumberResponse: + """ + Register a phone number as a sandbox number and associate it with a fixed + attempt code. Subsequent verification attempts against this number will not + trigger a real SMS/call and will validate against the configured attempt code. + + This operation is idempotent - re-adding the same phone number will overwrite + the existing attempt code. + + In order to get access to this endpoint, contact our support team. + + Args: + attempt_code: The fixed attempt code that will validate verification attempts for this phone + number. + + phone_number: An E.164 formatted phone number to add to the sandbox list. + + 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 + """ + return await self._put( + "/v2/verification/management/phone-numbers/sandbox", + body=await async_maybe_transform( + { + "attempt_code": attempt_code, + "phone_number": phone_number, + }, + sandbox_add_phone_number_params.SandboxAddPhoneNumberParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SandboxAddPhoneNumberResponse, + ) + + async def delete_phone_number( + self, + phone_number: 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, + ) -> SandboxDeletePhoneNumberResponse: + """ + Remove a phone number from the sandbox list. + + This operation is idempotent - deleting a phone number that is not in the + sandbox list will succeed without making any changes. + + In order to get access to this endpoint, contact our support team. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not phone_number: + raise ValueError(f"Expected a non-empty value for `phone_number` but received {phone_number!r}") + return await self._delete( + path_template( + "/v2/verification/management/phone-numbers/sandbox/{phone_number}", phone_number=phone_number + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SandboxDeletePhoneNumberResponse, + ) + + async def list_phone_numbers( + self, + *, + # 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, + ) -> SandboxListPhoneNumbersResponse: + """Retrieve the list of sandbox phone numbers for the account. + + Sandbox numbers are + test numbers that bypass the real verification flow and return a fixed attempt + code. + + In order to get access to this endpoint, contact our support team. + """ + return await self._get( + "/v2/verification/management/phone-numbers/sandbox", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SandboxListPhoneNumbersResponse, + ) + + +class SandboxResourceWithRawResponse: + def __init__(self, sandbox: SandboxResource) -> None: + self._sandbox = sandbox + + self.add_phone_number = to_raw_response_wrapper( + sandbox.add_phone_number, + ) + self.delete_phone_number = to_raw_response_wrapper( + sandbox.delete_phone_number, + ) + self.list_phone_numbers = to_raw_response_wrapper( + sandbox.list_phone_numbers, + ) + + +class AsyncSandboxResourceWithRawResponse: + def __init__(self, sandbox: AsyncSandboxResource) -> None: + self._sandbox = sandbox + + self.add_phone_number = async_to_raw_response_wrapper( + sandbox.add_phone_number, + ) + self.delete_phone_number = async_to_raw_response_wrapper( + sandbox.delete_phone_number, + ) + self.list_phone_numbers = async_to_raw_response_wrapper( + sandbox.list_phone_numbers, + ) + + +class SandboxResourceWithStreamingResponse: + def __init__(self, sandbox: SandboxResource) -> None: + self._sandbox = sandbox + + self.add_phone_number = to_streamed_response_wrapper( + sandbox.add_phone_number, + ) + self.delete_phone_number = to_streamed_response_wrapper( + sandbox.delete_phone_number, + ) + self.list_phone_numbers = to_streamed_response_wrapper( + sandbox.list_phone_numbers, + ) + + +class AsyncSandboxResourceWithStreamingResponse: + def __init__(self, sandbox: AsyncSandboxResource) -> None: + self._sandbox = sandbox + + self.add_phone_number = async_to_streamed_response_wrapper( + sandbox.add_phone_number, + ) + self.delete_phone_number = async_to_streamed_response_wrapper( + sandbox.delete_phone_number, + ) + self.list_phone_numbers = async_to_streamed_response_wrapper( + sandbox.list_phone_numbers, + ) diff --git a/src/prelude_python_sdk/resources/verification_management.py b/src/prelude_python_sdk/resources/verification_management/verification_management.py similarity index 90% rename from src/prelude_python_sdk/resources/verification_management.py rename to src/prelude_python_sdk/resources/verification_management/verification_management.py index 244e7416..dfd3d49c 100644 --- a/src/prelude_python_sdk/resources/verification_management.py +++ b/src/prelude_python_sdk/resources/verification_management/verification_management.py @@ -6,27 +6,37 @@ import httpx -from ..types import ( +from ...types import ( verification_management_set_phone_number_params, verification_management_submit_sender_id_params, verification_management_delete_phone_number_params, ) -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 ( +from .sandbox import ( + SandboxResource, + AsyncSandboxResource, + SandboxResourceWithRawResponse, + AsyncSandboxResourceWithRawResponse, + SandboxResourceWithStreamingResponse, + AsyncSandboxResourceWithStreamingResponse, +) +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 .._base_client import make_request_options -from ..types.verification_management_list_sender_ids_response import VerificationManagementListSenderIDsResponse -from ..types.verification_management_set_phone_number_response import VerificationManagementSetPhoneNumberResponse -from ..types.verification_management_submit_sender_id_response import VerificationManagementSubmitSenderIDResponse -from ..types.verification_management_list_phone_numbers_response import VerificationManagementListPhoneNumbersResponse -from ..types.verification_management_delete_phone_number_response import VerificationManagementDeletePhoneNumberResponse +from ..._base_client import make_request_options +from ...types.verification_management_list_sender_ids_response import VerificationManagementListSenderIDsResponse +from ...types.verification_management_set_phone_number_response import VerificationManagementSetPhoneNumberResponse +from ...types.verification_management_submit_sender_id_response import VerificationManagementSubmitSenderIDResponse +from ...types.verification_management_list_phone_numbers_response import VerificationManagementListPhoneNumbersResponse +from ...types.verification_management_delete_phone_number_response import ( + VerificationManagementDeletePhoneNumberResponse, +) __all__ = ["VerificationManagementResource", "AsyncVerificationManagementResource"] @@ -34,6 +44,11 @@ class VerificationManagementResource(SyncAPIResource): """Verify phone numbers.""" + @cached_property + def sandbox(self) -> SandboxResource: + """Verify phone numbers.""" + return SandboxResource(self._client) + @cached_property def with_raw_response(self) -> VerificationManagementResourceWithRawResponse: """ @@ -246,6 +261,11 @@ def submit_sender_id( class AsyncVerificationManagementResource(AsyncAPIResource): """Verify phone numbers.""" + @cached_property + def sandbox(self) -> AsyncSandboxResource: + """Verify phone numbers.""" + return AsyncSandboxResource(self._client) + @cached_property def with_raw_response(self) -> AsyncVerificationManagementResourceWithRawResponse: """ @@ -475,6 +495,11 @@ def __init__(self, verification_management: VerificationManagementResource) -> N verification_management.submit_sender_id, ) + @cached_property + def sandbox(self) -> SandboxResourceWithRawResponse: + """Verify phone numbers.""" + return SandboxResourceWithRawResponse(self._verification_management.sandbox) + class AsyncVerificationManagementResourceWithRawResponse: def __init__(self, verification_management: AsyncVerificationManagementResource) -> None: @@ -496,6 +521,11 @@ def __init__(self, verification_management: AsyncVerificationManagementResource) verification_management.submit_sender_id, ) + @cached_property + def sandbox(self) -> AsyncSandboxResourceWithRawResponse: + """Verify phone numbers.""" + return AsyncSandboxResourceWithRawResponse(self._verification_management.sandbox) + class VerificationManagementResourceWithStreamingResponse: def __init__(self, verification_management: VerificationManagementResource) -> None: @@ -517,6 +547,11 @@ def __init__(self, verification_management: VerificationManagementResource) -> N verification_management.submit_sender_id, ) + @cached_property + def sandbox(self) -> SandboxResourceWithStreamingResponse: + """Verify phone numbers.""" + return SandboxResourceWithStreamingResponse(self._verification_management.sandbox) + class AsyncVerificationManagementResourceWithStreamingResponse: def __init__(self, verification_management: AsyncVerificationManagementResource) -> None: @@ -537,3 +572,8 @@ def __init__(self, verification_management: AsyncVerificationManagementResource) self.submit_sender_id = async_to_streamed_response_wrapper( verification_management.submit_sender_id, ) + + @cached_property + def sandbox(self) -> AsyncSandboxResourceWithStreamingResponse: + """Verify phone numbers.""" + return AsyncSandboxResourceWithStreamingResponse(self._verification_management.sandbox) diff --git a/src/prelude_python_sdk/resources/watch.py b/src/prelude_python_sdk/resources/watch.py index 9e126fc3..a4e70adc 100644 --- a/src/prelude_python_sdk/resources/watch.py +++ b/src/prelude_python_sdk/resources/watch.py @@ -2,11 +2,16 @@ from __future__ import annotations -from typing import Iterable +from typing import Dict, Iterable import httpx -from ..types import watch_predict_params, watch_send_events_params, watch_send_feedbacks_params +from ..types import ( + watch_predict_params, + watch_evaluate_params, + watch_send_events_params, + watch_send_feedbacks_params, +) from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property @@ -18,7 +23,10 @@ async_to_streamed_response_wrapper, ) from .._base_client import make_request_options +from ..types.shared_params.target import Target +from ..types.shared_params.signals import Signals from ..types.watch_predict_response import WatchPredictResponse +from ..types.watch_evaluate_response import WatchEvaluateResponse from ..types.watch_send_events_response import WatchSendEventsResponse from ..types.watch_send_feedbacks_response import WatchSendFeedbacksResponse @@ -47,13 +55,89 @@ def with_streaming_response(self) -> WatchResourceWithStreamingResponse: """ return WatchResourceWithStreamingResponse(self) + def evaluate( + self, + *, + flow_id: str, + target: Target, + attributes: Dict[str, str] | Omit = omit, + dispatch_id: str | Omit = omit, + signals: Signals | 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, + ) -> WatchEvaluateResponse: + """ + **Beta.** The request and response shapes may still change, and flows and + recipes are configured by Prelude on your behalf for now. Talk to us before you + build against it. + + Score a target against the rules configured for one moment in your product — + signup, checkout, password reset. The flow selects which recipes run; each + recipe scores its rules against a threshold and returns its own verdict, and the + evaluation answers with the most severe verdict and action across them. Where + Predict returns a single model-derived outcome, Eval returns the full breakdown, + so you can see which rules fired and which could not run. Scoring-only — it does + not update counters by itself. + + Args: + flow_id: The flow to evaluate. A flow names the moment you are guarding and selects the + recipes that run. + + target: The identifier to score — a phone number or email address. + + attributes: Values for the attributes the flow's recipes declare, keyed without the `attr.` + namespace a rule uses to reference them. + + An attribute a recipe declares and this request omits is treated as missing + evidence, not as an empty value: the rules reading it report `NOT_EVALUATED` + rather than being scored as though the condition were false. A key no recipe in + the flow declares is ignored rather than rejected, so one payload can serve + flows that read different attributes. + + dispatch_id: The identifier of the dispatch that came from the front-end SDK. Signals it + carries fill in anything the request did not state; the request wins where both + supply a value. + + signals: The signals used for anti-fraud. For more details, refer to + [Signals](/verify/v2/documentation/prevent-fraud#signals). + + 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 + """ + return self._post( + "/v2/watch/eval", + body=maybe_transform( + { + "flow_id": flow_id, + "target": target, + "attributes": attributes, + "dispatch_id": dispatch_id, + "signals": signals, + }, + watch_evaluate_params.WatchEvaluateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=WatchEvaluateResponse, + ) + def predict( self, *, - target: watch_predict_params.Target, + target: Target, dispatch_id: str | Omit = omit, metadata: watch_predict_params.Metadata | Omit = omit, - signals: watch_predict_params.Signals | Omit = omit, + signals: Signals | 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, @@ -208,13 +292,89 @@ def with_streaming_response(self) -> AsyncWatchResourceWithStreamingResponse: """ return AsyncWatchResourceWithStreamingResponse(self) + async def evaluate( + self, + *, + flow_id: str, + target: Target, + attributes: Dict[str, str] | Omit = omit, + dispatch_id: str | Omit = omit, + signals: Signals | 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, + ) -> WatchEvaluateResponse: + """ + **Beta.** The request and response shapes may still change, and flows and + recipes are configured by Prelude on your behalf for now. Talk to us before you + build against it. + + Score a target against the rules configured for one moment in your product — + signup, checkout, password reset. The flow selects which recipes run; each + recipe scores its rules against a threshold and returns its own verdict, and the + evaluation answers with the most severe verdict and action across them. Where + Predict returns a single model-derived outcome, Eval returns the full breakdown, + so you can see which rules fired and which could not run. Scoring-only — it does + not update counters by itself. + + Args: + flow_id: The flow to evaluate. A flow names the moment you are guarding and selects the + recipes that run. + + target: The identifier to score — a phone number or email address. + + attributes: Values for the attributes the flow's recipes declare, keyed without the `attr.` + namespace a rule uses to reference them. + + An attribute a recipe declares and this request omits is treated as missing + evidence, not as an empty value: the rules reading it report `NOT_EVALUATED` + rather than being scored as though the condition were false. A key no recipe in + the flow declares is ignored rather than rejected, so one payload can serve + flows that read different attributes. + + dispatch_id: The identifier of the dispatch that came from the front-end SDK. Signals it + carries fill in anything the request did not state; the request wins where both + supply a value. + + signals: The signals used for anti-fraud. For more details, refer to + [Signals](/verify/v2/documentation/prevent-fraud#signals). + + 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 + """ + return await self._post( + "/v2/watch/eval", + body=await async_maybe_transform( + { + "flow_id": flow_id, + "target": target, + "attributes": attributes, + "dispatch_id": dispatch_id, + "signals": signals, + }, + watch_evaluate_params.WatchEvaluateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=WatchEvaluateResponse, + ) + async def predict( self, *, - target: watch_predict_params.Target, + target: Target, dispatch_id: str | Omit = omit, metadata: watch_predict_params.Metadata | Omit = omit, - signals: watch_predict_params.Signals | Omit = omit, + signals: Signals | 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, @@ -353,6 +513,9 @@ class WatchResourceWithRawResponse: def __init__(self, watch: WatchResource) -> None: self._watch = watch + self.evaluate = to_raw_response_wrapper( + watch.evaluate, + ) self.predict = to_raw_response_wrapper( watch.predict, ) @@ -368,6 +531,9 @@ class AsyncWatchResourceWithRawResponse: def __init__(self, watch: AsyncWatchResource) -> None: self._watch = watch + self.evaluate = async_to_raw_response_wrapper( + watch.evaluate, + ) self.predict = async_to_raw_response_wrapper( watch.predict, ) @@ -383,6 +549,9 @@ class WatchResourceWithStreamingResponse: def __init__(self, watch: WatchResource) -> None: self._watch = watch + self.evaluate = to_streamed_response_wrapper( + watch.evaluate, + ) self.predict = to_streamed_response_wrapper( watch.predict, ) @@ -398,6 +567,9 @@ class AsyncWatchResourceWithStreamingResponse: def __init__(self, watch: AsyncWatchResource) -> None: self._watch = watch + self.evaluate = async_to_streamed_response_wrapper( + watch.evaluate, + ) self.predict = async_to_streamed_response_wrapper( watch.predict, ) diff --git a/src/prelude_python_sdk/types/__init__.py b/src/prelude_python_sdk/types/__init__.py index f8619cb7..e3994fe5 100644 --- a/src/prelude_python_sdk/types/__init__.py +++ b/src/prelude_python_sdk/types/__init__.py @@ -2,12 +2,17 @@ from __future__ import annotations +from .shared import Target as Target, Signals as Signals from .notify_send_params import NotifySendParams as NotifySendParams +from .notify_reply_params import NotifyReplyParams as NotifyReplyParams from .lookup_lookup_params import LookupLookupParams as LookupLookupParams from .notify_send_response import NotifySendResponse as NotifySendResponse from .watch_predict_params import WatchPredictParams as WatchPredictParams +from .notify_reply_response import NotifyReplyResponse as NotifyReplyResponse +from .watch_evaluate_params import WatchEvaluateParams as WatchEvaluateParams from .lookup_lookup_response import LookupLookupResponse as LookupLookupResponse from .watch_predict_response import WatchPredictResponse as WatchPredictResponse +from .watch_evaluate_response import WatchEvaluateResponse as WatchEvaluateResponse from .notify_send_batch_params import NotifySendBatchParams as NotifySendBatchParams from .watch_send_events_params import WatchSendEventsParams as WatchSendEventsParams from .transactional_send_params import TransactionalSendParams as TransactionalSendParams diff --git a/src/prelude_python_sdk/types/intel/__init__.py b/src/prelude_python_sdk/types/intel/__init__.py new file mode 100644 index 00000000..7762c407 --- /dev/null +++ b/src/prelude_python_sdk/types/intel/__init__.py @@ -0,0 +1,6 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .kyc_match_params import KYCMatchParams as KYCMatchParams +from .kyc_match_response import KYCMatchResponse as KYCMatchResponse diff --git a/src/prelude_python_sdk/types/intel/kyc_match_params.py b/src/prelude_python_sdk/types/intel/kyc_match_params.py new file mode 100644 index 00000000..3f7ab0d3 --- /dev/null +++ b/src/prelude_python_sdk/types/intel/kyc_match_params.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import date +from typing_extensions import Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["KYCMatchParams"] + + +class KYCMatchParams(TypedDict, total=False): + address: str + """The street address.""" + + birthdate: Annotated[Union[str, date], PropertyInfo(format="iso8601")] + """The date of birth in ISO 8601 (`YYYY-MM-DD`) format. Compared exactly.""" + + country: str + """The ISO 3166-1 alpha-2 country code. Compared exactly.""" + + email: str + """The email address.""" + + family_name: str + """The end-user's family (last) name.""" + + given_name: str + """The end-user's given (first) name.""" + + locality: str + """The locality (city).""" + + postal_code: str + """The postal code. Compared exactly.""" + + region: str + """The region, state, or province.""" diff --git a/src/prelude_python_sdk/types/intel/kyc_match_response.py b/src/prelude_python_sdk/types/intel/kyc_match_response.py new file mode 100644 index 00000000..e67148c8 --- /dev/null +++ b/src/prelude_python_sdk/types/intel/kyc_match_response.py @@ -0,0 +1,84 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["KYCMatchResponse"] + + +class KYCMatchResponse(BaseModel): + """The per-attribute match result. + + Each `_match` field is one of `true`, `false`, or `not_available` (the operator could not answer for that attribute). Fuzzy attributes additionally return a `_match_score` (0-99 similarity) when they do not match exactly; the score is omitted on a match or when `not_available`. + """ + + address_match: Optional[Literal["true", "false", "not_available"]] = None + """Whether the street address matched the operator's record.""" + + address_match_score: Optional[int] = None + """Similarity score (0-99) for the address. Returned only on a non-match.""" + + birthdate_match: Optional[Literal["true", "false", "not_available"]] = None + """Whether the date of birth matched the operator's record. + + Compared exactly; never scored. + """ + + country_code: Optional[str] = None + """The country code of the phone number.""" + + country_match: Optional[Literal["true", "false", "not_available"]] = None + """Whether the country matched the operator's record. + + Compared exactly; never scored. + """ + + email_match: Optional[Literal["true", "false", "not_available"]] = None + """Whether the email address matched the operator's record.""" + + email_match_score: Optional[int] = None + """Similarity score (0-99) for the email. Returned only on a non-match.""" + + family_name_match: Optional[Literal["true", "false", "not_available"]] = None + """Whether the family name matched the operator's record.""" + + family_name_match_score: Optional[int] = None + """Similarity score (0-99) for the family name. Returned only on a non-match.""" + + given_name_match: Optional[Literal["true", "false", "not_available"]] = None + """Whether the given name matched the operator's record.""" + + given_name_match_score: Optional[int] = None + """Similarity score (0-99) for the given name. Returned only on a non-match.""" + + locality_match: Optional[Literal["true", "false", "not_available"]] = None + """Whether the locality matched the operator's record.""" + + locality_match_score: Optional[int] = None + """Similarity score (0-99) for the locality. Returned only on a non-match.""" + + operator: Optional[str] = None + """The mobile operator that answered the match.""" + + phone_number: Optional[str] = None + """The phone number that was matched, in E.164 format.""" + + postal_code_match: Optional[Literal["true", "false", "not_available"]] = None + """Whether the postal code matched the operator's record. + + Compared exactly; never scored. + """ + + region_match: Optional[Literal["true", "false", "not_available"]] = None + """Whether the region matched the operator's record.""" + + region_match_score: Optional[int] = None + """Similarity score (0-99) for the region. Returned only on a non-match.""" + + request_id: Optional[str] = None + """A string that identifies this specific request. + + Report it back to us to help us diagnose your issues. + """ diff --git a/src/prelude_python_sdk/types/notify_reply_params.py b/src/prelude_python_sdk/types/notify_reply_params.py new file mode 100644 index 00000000..9383016f --- /dev/null +++ b/src/prelude_python_sdk/types/notify_reply_params.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["NotifyReplyParams"] + + +class NotifyReplyParams(TypedDict, total=False): + reply_to: Required[str] + """The inbound message ID (prefixed with `im_`) to reply to. + + This ID is provided in the `inbound.message.received` webhook event. + """ + + text: Required[str] + """The reply message body sent as a free-form WhatsApp text.""" + + to: Required[str] + """The recipient's phone number in E.164 format. + + Must match the phone number that sent the original inbound message. + """ + + callback_url: str + """The URL where webhooks will be sent for delivery events of this reply.""" + + correlation_id: str + """A user-defined identifier to correlate this reply with your internal systems. + + It is returned in the response and any webhook events that refer to this + message. + """ diff --git a/src/prelude_python_sdk/types/notify_reply_response.py b/src/prelude_python_sdk/types/notify_reply_response.py new file mode 100644 index 00000000..79e34865 --- /dev/null +++ b/src/prelude_python_sdk/types/notify_reply_response.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime + +from .._models import BaseModel + +__all__ = ["NotifyReplyResponse"] + + +class NotifyReplyResponse(BaseModel): + id: str + """The reply message identifier.""" + + created_at: datetime + """The reply creation date in RFC3339 format.""" + + reply_to: str + """The inbound message ID this reply was sent in response to.""" + + text: str + """The reply message body that was sent.""" + + to: str + """The recipient's phone number in E.164 format.""" + + callback_url: Optional[str] = None + """The callback URL where webhooks will be sent.""" + + correlation_id: Optional[str] = None + """The user-defined correlation identifier echoed back from the request.""" diff --git a/src/prelude_python_sdk/types/shared/__init__.py b/src/prelude_python_sdk/types/shared/__init__.py new file mode 100644 index 00000000..abf0e8bb --- /dev/null +++ b/src/prelude_python_sdk/types/shared/__init__.py @@ -0,0 +1,4 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .target import Target as Target +from .signals import Signals as Signals diff --git a/src/prelude_python_sdk/types/shared/signals.py b/src/prelude_python_sdk/types/shared/signals.py new file mode 100644 index 00000000..73864312 --- /dev/null +++ b/src/prelude_python_sdk/types/shared/signals.py @@ -0,0 +1,75 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["Signals"] + + +class Signals(BaseModel): + """The signals used for anti-fraud. + + For more details, refer to [Signals](/verify/v2/documentation/prevent-fraud#signals). + """ + + app_version: Optional[str] = None + """The version of your application.""" + + device_id: Optional[str] = None + """A unique ID for the user's device. + + You should ensure that each user device has a unique `device_id` value. Ideally, + for Android, this corresponds to the `ANDROID_ID` and for iOS, this corresponds + to the `identifierForVendor`. + """ + + device_model: Optional[str] = None + """The model of the user's device.""" + + device_platform: Optional[Literal["android", "ios", "ipados", "tvos", "web"]] = None + """The type of the user's device.""" + + existing_user: Optional[bool] = None + """ + Whether the end-user already exists in your system, for example an existing + account signing in again rather than a first-time signup. Unlike + `is_trusted_user`, this signal does not bypass fraud checks; it is taken into + account as one additional anti-fraud signal. For more details, refer to + [Signals](/verify/v2/documentation/prevent-fraud#signals). + """ + + ip: Optional[str] = None + """The public IP v4 or v6 address of the end-user's device. + + You should collect this from your backend. If your backend is behind a proxy, + use the `X-Forwarded-For`, `Forwarded`, `True-Client-IP`, `CF-Connecting-IP` or + an equivalent header to get the actual public IP of the end-user's device. + """ + + is_trusted_user: Optional[bool] = None + """ + This signal should indicate a higher level of trust, explicitly stating that the + user is genuine. Contact us to discuss your use case. For more details, refer to + [Signals](/verify/v2/documentation/prevent-fraud#signals). + """ + + ja4_fingerprint: Optional[str] = None + """The JA4 fingerprint observed for the end-user's connection. + + Prelude will infer it automatically when you use our Frontend SDKs (which use + Prelude's edge network), but you can also forward the value if you terminate TLS + yourself. + """ + + os_version: Optional[str] = None + """The version of the user's device operating system.""" + + user_agent: Optional[str] = None + """The user agent of the user's device. + + If the individual fields (os_version, device_platform, device_model) are + provided, we will prioritize those values instead of parsing them from the user + agent string. + """ diff --git a/src/prelude_python_sdk/types/shared/target.py b/src/prelude_python_sdk/types/shared/target.py new file mode 100644 index 00000000..34d6035e --- /dev/null +++ b/src/prelude_python_sdk/types/shared/target.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["Target"] + + +class Target(BaseModel): + """The operation target. Either a phone number or an email address.""" + + type: Literal["phone_number", "email_address"] + """The type of the target. Either "phone_number" or "email_address".""" + + value: str + """An E.164 formatted phone number or an email address.""" diff --git a/src/prelude_python_sdk/types/shared_params/__init__.py b/src/prelude_python_sdk/types/shared_params/__init__.py new file mode 100644 index 00000000..abf0e8bb --- /dev/null +++ b/src/prelude_python_sdk/types/shared_params/__init__.py @@ -0,0 +1,4 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .target import Target as Target +from .signals import Signals as Signals diff --git a/src/prelude_python_sdk/types/shared_params/signals.py b/src/prelude_python_sdk/types/shared_params/signals.py new file mode 100644 index 00000000..19288201 --- /dev/null +++ b/src/prelude_python_sdk/types/shared_params/signals.py @@ -0,0 +1,74 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["Signals"] + + +class Signals(TypedDict, total=False): + """The signals used for anti-fraud. + + For more details, refer to [Signals](/verify/v2/documentation/prevent-fraud#signals). + """ + + app_version: str + """The version of your application.""" + + device_id: str + """A unique ID for the user's device. + + You should ensure that each user device has a unique `device_id` value. Ideally, + for Android, this corresponds to the `ANDROID_ID` and for iOS, this corresponds + to the `identifierForVendor`. + """ + + device_model: str + """The model of the user's device.""" + + device_platform: Literal["android", "ios", "ipados", "tvos", "web"] + """The type of the user's device.""" + + existing_user: bool + """ + Whether the end-user already exists in your system, for example an existing + account signing in again rather than a first-time signup. Unlike + `is_trusted_user`, this signal does not bypass fraud checks; it is taken into + account as one additional anti-fraud signal. For more details, refer to + [Signals](/verify/v2/documentation/prevent-fraud#signals). + """ + + ip: str + """The public IP v4 or v6 address of the end-user's device. + + You should collect this from your backend. If your backend is behind a proxy, + use the `X-Forwarded-For`, `Forwarded`, `True-Client-IP`, `CF-Connecting-IP` or + an equivalent header to get the actual public IP of the end-user's device. + """ + + is_trusted_user: bool + """ + This signal should indicate a higher level of trust, explicitly stating that the + user is genuine. Contact us to discuss your use case. For more details, refer to + [Signals](/verify/v2/documentation/prevent-fraud#signals). + """ + + ja4_fingerprint: str + """The JA4 fingerprint observed for the end-user's connection. + + Prelude will infer it automatically when you use our Frontend SDKs (which use + Prelude's edge network), but you can also forward the value if you terminate TLS + yourself. + """ + + os_version: str + """The version of the user's device operating system.""" + + user_agent: str + """The user agent of the user's device. + + If the individual fields (os_version, device_platform, device_model) are + provided, we will prioritize those values instead of parsing them from the user + agent string. + """ diff --git a/src/prelude_python_sdk/types/shared_params/target.py b/src/prelude_python_sdk/types/shared_params/target.py new file mode 100644 index 00000000..f5ca9915 --- /dev/null +++ b/src/prelude_python_sdk/types/shared_params/target.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["Target"] + + +class Target(TypedDict, total=False): + """The operation target. Either a phone number or an email address.""" + + type: Required[Literal["phone_number", "email_address"]] + """The type of the target. Either "phone_number" or "email_address".""" + + value: Required[str] + """An E.164 formatted phone number or an email address.""" diff --git a/src/prelude_python_sdk/types/verification/__init__.py b/src/prelude_python_sdk/types/verification/__init__.py new file mode 100644 index 00000000..f8ee8b14 --- /dev/null +++ b/src/prelude_python_sdk/types/verification/__init__.py @@ -0,0 +1,3 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations diff --git a/src/prelude_python_sdk/types/verification/phone/__init__.py b/src/prelude_python_sdk/types/verification/phone/__init__.py new file mode 100644 index 00000000..ea66eb09 --- /dev/null +++ b/src/prelude_python_sdk/types/verification/phone/__init__.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .history_list_params import HistoryListParams as HistoryListParams +from .history_list_response import HistoryListResponse as HistoryListResponse +from .phone_verification_money import PhoneVerificationMoney as PhoneVerificationMoney +from .history_retrieve_response import HistoryRetrieveResponse as HistoryRetrieveResponse +from .phone_verification_carrier import PhoneVerificationCarrier as PhoneVerificationCarrier +from .phone_verification_psd2_transaction import PhoneVerificationPsd2Transaction as PhoneVerificationPsd2Transaction diff --git a/src/prelude_python_sdk/types/verification/phone/history_list_params.py b/src/prelude_python_sdk/types/verification/phone/history_list_params.py new file mode 100644 index 00000000..87aa6b9e --- /dev/null +++ b/src/prelude_python_sdk/types/verification/phone/history_list_params.py @@ -0,0 +1,85 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union +from datetime import datetime +from typing_extensions import Literal, Annotated, TypedDict + +from ...._utils import PropertyInfo + +__all__ = ["HistoryListParams"] + + +class HistoryListParams(TypedDict, total=False): + channels: List[Literal["sms", "rcs", "whatsapp", "viber", "zalo", "telegram", "voice", "silent"]] + """Only verifications that could use one of these channels. + + Repeat the parameter for several values. + """ + + cursor: str + """Pagination cursor from the previous response.""" + + device_platform: Literal["android", "ios", "ipados", "tvos", "web"] + """Only verifications created from this device platform.""" + + from_: Annotated[Union[str, datetime], PropertyInfo(alias="from", format="iso8601")] + """Only verifications created at or after this RFC 3339 timestamp. + + Goes with `to`, at most 6 months apart. Without them the whole history is + searched. + """ + + limit: int + """Maximum number of verifications to return per page.""" + + max_attempts: int + """Only verifications that sent at most this many messages. + + `0` keeps the verifications that never sent one. + """ + + min_attempts: int + """Only verifications that sent at least this many messages.""" + + phone_number: str + """Only verifications targeting this E.164 phone number. + + The leading `+` may be omitted. + """ + + region: str + """ + Only verifications of phone numbers from this region, as an ISO 3166-1 alpha-2 + code. + """ + + status: Literal[ + "converted", + "not_converted", + "pending_check", + "sent", + "challenged", + "suspected_fraud", + "in_blocklist", + "invalid_line", + "invalid_number", + "rate_limited", + "expired_signals", + "shadowed", + ] + """Only verifications in this status. `pending_check` cannot be filtered on.""" + + template_id: str + """ + Only verifications sent with this template, as returned in `template_id` by + [Get a phone verification](/verify/v2/api-reference/history/get-a-phone-verification). + Built-in templates (`prelude:*`) cannot be filtered on. + """ + + to: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] + """Only verifications created at or before this RFC 3339 timestamp. + + Goes with `from`. + """ diff --git a/src/prelude_python_sdk/types/verification/phone/history_list_response.py b/src/prelude_python_sdk/types/verification/phone/history_list_response.py new file mode 100644 index 00000000..83b6d075 --- /dev/null +++ b/src/prelude_python_sdk/types/verification/phone/history_list_response.py @@ -0,0 +1,113 @@ +# 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 +from .phone_verification_money import PhoneVerificationMoney + +__all__ = ["HistoryListResponse", "Verification", "VerificationChannel"] + + +class VerificationChannel(BaseModel): + channel: Literal["sms", "rcs", "whatsapp", "viber", "zalo", "telegram", "voice", "silent"] + + converted: bool + """Whether the end user submitted a valid code received through this channel.""" + + +class Verification(BaseModel): + """One entry of the verification history. + + [Get a phone verification](/verify/v2/api-reference/history/get-a-phone-verification) returns the full record. + """ + + id: str + """The verification identifier.""" + + channels: List[VerificationChannel] + """ + The channels the verification could use, and which one the end user converted + through. Empty when the verification used only channels this API does not list. + """ + + created_at: datetime + + delivered: bool + """Whether at least one message was reported delivered.""" + + phone_number: str + """The E.164 phone number the verification targeted.""" + + status: Literal[ + "converted", + "not_converted", + "pending_check", + "sent", + "challenged", + "suspected_fraud", + "in_blocklist", + "invalid_line", + "invalid_number", + "rate_limited", + "expired_signals", + "shadowed", + ] + """The outcome of the verification. + + - `converted` - The end user submitted a valid code. + - `not_converted` - The verification expired without a valid code. + - `pending_check` - A code was delivered and Prelude is still waiting for a + check. + - `sent` - A code was sent and the verification window is still open. + - `challenged` - The verification was restricted to non-SMS and non-voice + channels. + - `suspected_fraud` - The anti-fraud system blocked the verification. + - `in_blocklist` - The phone number is on the configured block list. + - `invalid_line` - The phone number is not a valid line type. + - `invalid_number` - The phone number is not a valid number. + - `rate_limited` - The verification was refused by a rate limit. + - `expired_signals` - The SDK signals were collected too long before the + request. + - `shadowed` - The anti-fraud system flagged the verification without blocking + it. + """ + + attempts: Optional[int] = None + """Number of messages sent for the verification, `0` when none was. + + Absent for sandboxed phone numbers. + """ + + converted_at: Optional[datetime] = None + """When the end user submitted a valid code. + + Absent unless the verification converted. + """ + + cost: Optional[PhoneVerificationMoney] = None + """Total cost of the verification. Absent when nothing was billed.""" + + device_platform: Optional[Literal["android", "ios", "ipados", "tvos", "web"]] = None + """Platform of the end-user device, when known.""" + + phone_number_condition: Optional[Literal["allow_listed", "block_listed", "sandboxed"]] = None + """ + Whether the phone number was allow-listed, block-listed, or sandboxed at + verification time. + """ + + signals_hash_status: Optional[Literal["valid", "invalid"]] = None + """Whether the SDK signals integrity check passed.""" + + +class HistoryListResponse(BaseModel): + verifications: List[Verification] + """The page of verifications, most recent first.""" + + next_cursor: Optional[str] = None + """Pagination cursor for the next page of results. + + Omitted if there are no more pages. + """ diff --git a/src/prelude_python_sdk/types/verification/phone/history_retrieve_response.py b/src/prelude_python_sdk/types/verification/phone/history_retrieve_response.py new file mode 100644 index 00000000..e2b8de1f --- /dev/null +++ b/src/prelude_python_sdk/types/verification/phone/history_retrieve_response.py @@ -0,0 +1,333 @@ +# 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 +from .phone_verification_money import PhoneVerificationMoney +from .phone_verification_carrier import PhoneVerificationCarrier +from .phone_verification_psd2_transaction import PhoneVerificationPsd2Transaction + +__all__ = [ + "HistoryRetrieveResponse", + "Lifecycle", + "LifecycleEvent", + "LifecycleEventAttempt", + "LifecycleEventAttemptDeliveryEvent", + "LifecycleEventCheck", + "LifecycleEventCheckPsd2Info", + "LifecycleEventCreate", + "LifecycleEventSignals", + "Signals", +] + + +class LifecycleEventAttemptDeliveryEvent(BaseModel): + received_at: datetime + + status: Literal[ + "unknown", + "submitted", + "in_transit", + "delivered", + "undeliverable", + "expired", + "read", + "silent_started", + "silent_verified", + "silent_mismatch", + ] + """The state this event reported. + + It is finer-grained than the attempt's `delivery_status` and includes the states + a silent verification goes through. + """ + + +class LifecycleEventAttempt(BaseModel): + """One message sent for this verification.""" + + id: str + + created_at: datetime + + carrier: Optional[PhoneVerificationCarrier] = None + """The end user's mobile network.""" + + channel: Optional[Literal["sms", "rcs", "whatsapp", "viber", "zalo", "telegram", "voice", "silent"]] = None + + content: Optional[str] = None + """Message body. + + While the verification can still be completed, the code inside it is masked + rather than removed. + """ + + cost: Optional[PhoneVerificationMoney] = None + + delivery_events: Optional[List[LifecycleEventAttemptDeliveryEvent]] = None + + delivery_status: Optional[Literal["unknown", "in_transit", "delivered", "undeliverable", "read"]] = None + + preferred_channel: Optional[Literal["sms", "rcs", "whatsapp", "viber", "zalo", "telegram", "voice", "silent"]] = ( + None + ) + """Channel you asked for, when it differs from the one used.""" + + status: Optional[Literal["succeeded", "failed"]] = None + + trigger: Optional[Literal["initial", "auto_retry", "user_retry"]] = None + """What caused the attempt.""" + + +class LifecycleEventCheckPsd2Info(BaseModel): + """Present on checks against a `prelude:psd2` code.""" + + expected_transaction: Optional[PhoneVerificationPsd2Transaction] = None + """The transaction submitted when the code was issued.""" + + received_transaction: Optional[PhoneVerificationPsd2Transaction] = None + """The transaction submitted with this check. + + Differs from `expected_transaction` when `status_detail` is + `transaction_mismatch`. + """ + + +class LifecycleEventCheck(BaseModel): + """One code submission for this verification.""" + + created_at: datetime + + is_valid: bool + + channel: Optional[Literal["sms", "rcs", "whatsapp", "viber", "zalo", "telegram", "voice", "silent"]] = None + + psd2_info: Optional[LifecycleEventCheckPsd2Info] = None + """Present on checks against a `prelude:psd2` code.""" + + status_detail: Optional[ + Literal["expired_attempt", "expired_auth", "rate_limited", "transaction_missing", "transaction_mismatch"] + ] = None + """Why an invalid check failed, when known.""" + + value: Optional[str] = None + """The submitted code. + + Absent while the verification can still be completed, so that a check in flight + cannot be read back through this endpoint, and absent on silent verification + checks, which carry no code. + """ + + +class LifecycleEventCreate(BaseModel): + created_at: datetime + + cost: Optional[PhoneVerificationMoney] = None + + +class LifecycleEventSignals(BaseModel): + received_at: datetime + + expired_at: Optional[datetime] = None + + status: Optional[Literal["valid", "invalid"]] = None + + +class LifecycleEvent(BaseModel): + """One timeline entry. `type` names the single payload field that is set.""" + + type: Literal["create", "attempt", "check", "signals"] + + attempt: Optional[LifecycleEventAttempt] = None + """One message sent for this verification.""" + + check: Optional[LifecycleEventCheck] = None + """One code submission for this verification.""" + + create: Optional[LifecycleEventCreate] = None + + signals: Optional[LifecycleEventSignals] = None + + +class Lifecycle(BaseModel): + """ + Chronological timeline of the verification: creation, message attempts with delivery events, code checks and signals reception. Omitted when Prelude holds no timeline for the verification. + """ + + events: List[LifecycleEvent] + + total_cost: Optional[PhoneVerificationMoney] = None + + undeliverable_route_count: Optional[int] = None + """How many times the message was reported undeliverable by independent routes. + + Above zero usually means the phone number is incorrect or the device + unreachable. + """ + + +class Signals(BaseModel): + """The anti-fraud signals you forwarded when creating the verification.""" + + is_trusted_user: bool + """Whether you flagged this end user as trusted when creating the verification. + + Declared by you, not computed by Prelude. + """ + + device_id: Optional[str] = None + """End-user device identifier you forwarded.""" + + ja4_fingerprint: Optional[str] = None + """TLS fingerprint you forwarded.""" + + os_version: Optional[str] = None + + user_agent: Optional[str] = None + + +class HistoryRetrieveResponse(BaseModel): + """A verification and everything Prelude recorded about it.""" + + id: str + """The verification identifier.""" + + created_at: datetime + + expires_at: datetime + + phone_number: str + """The E.164 phone number the verification targeted.""" + + status: Literal[ + "converted", + "not_converted", + "pending_check", + "sent", + "challenged", + "suspected_fraud", + "in_blocklist", + "invalid_line", + "invalid_number", + "rate_limited", + "expired_signals", + "shadowed", + ] + """The outcome of the verification. + + - `converted` - The end user submitted a valid code. + - `not_converted` - The verification expired without a valid code. + - `pending_check` - A code was delivered and Prelude is still waiting for a + check. + - `sent` - A code was sent and the verification window is still open. + - `challenged` - The verification was restricted to non-SMS and non-voice + channels. + - `suspected_fraud` - The anti-fraud system blocked the verification. + - `in_blocklist` - The phone number is on the configured block list. + - `invalid_line` - The phone number is not a valid line type. + - `invalid_number` - The phone number is not a valid number. + - `rate_limited` - The verification was refused by a rate limit. + - `expired_signals` - The SDK signals were collected too long before the + request. + - `shadowed` - The anti-fraud system flagged the verification without blocking + it. + """ + + app_version: Optional[str] = None + """Version of your application, when known.""" + + block_reasons: Optional[ + List[ + Literal[ + "behavioral_pattern", + "device_attribute", + "fraud_database", + "location_discrepancy", + "missing_signals", + "network_fingerprint", + "poor_conversion_history", + "prefix_concentration", + "repeated_number", + "suspected_request_tampering", + "suspicious_ip_address", + "temporary_phone_number", + ] + ] + ] = None + """Why the anti-fraud system blocked the verification. Empty unless it did. + + - `behavioral_pattern` - The phone number past behavior during verification + flows exhibits suspicious patterns. + - `device_attribute` - The end-user device reported attributes associated with + fraud or emulation. + - `fraud_database` - The phone number appears in a fraud database. + - `location_discrepancy` - The phone number region and the observed location + disagree. + - `missing_signals` - The verification expected Prelude SDK signals and none + arrived. + - `network_fingerprint` - The network fingerprint matches known fraudulent + traffic. + - `poor_conversion_history` - The phone number rarely completes the + verifications it starts. + - `prefix_concentration` - The phone number is part of a range known to be + associated with suspicious activity patterns. + - `repeated_number` - The phone number was used far more often than normal + traffic would explain. + - `suspected_request_tampering` - The SDK signals were altered or expired + between collection and use. + - `suspicious_ip_address` - The originating IP address is associated with + suspicious activity. + - `temporary_phone_number` - The phone number is known to be a temporary or + disposable number. + """ + + carrier: Optional[PhoneVerificationCarrier] = None + """The end user's mobile network.""" + + correlation_id: Optional[str] = None + """The correlation identifier you supplied when creating the verification.""" + + device_model: Optional[str] = None + """Model of the end-user device, when known.""" + + device_platform: Optional[Literal["android", "ios", "ipados", "tvos", "web"]] = None + """Platform of the end-user device, when known.""" + + ip_address: Optional[str] = None + """IP address the verification was created from.""" + + ip_address_region: Optional[str] = None + """ISO 3166-1 alpha-2 region of the caller's IP address.""" + + ip_distance_meters: Optional[int] = None + """Distance between the phone number region and the IP location.""" + + lifecycle: Optional[Lifecycle] = None + """ + Chronological timeline of the verification: creation, message attempts with + delivery events, code checks and signals reception. Omitted when Prelude holds + no timeline for the verification. + """ + + phone_number_condition: Optional[Literal["allow_listed", "block_listed", "sandboxed"]] = None + """ + Whether the phone number was allow-listed, block-listed, or sandboxed at + verification time. + """ + + phone_number_current_condition: Optional[Literal["allow_listed", "block_listed", "sandboxed"]] = None + """Whether the phone number is currently allow-listed, block-listed, or sandboxed.""" + + phone_number_region: Optional[str] = None + """ISO 3166-1 alpha-2 region of the phone number.""" + + signals: Optional[Signals] = None + """The anti-fraud signals you forwarded when creating the verification.""" + + signals_hash_status: Optional[Literal["valid", "invalid"]] = None + """Whether the SDK signals integrity check passed.""" + + template_id: Optional[str] = None + """The template used for this verification.""" diff --git a/src/prelude_python_sdk/types/verification/phone/phone_verification_carrier.py b/src/prelude_python_sdk/types/verification/phone/phone_verification_carrier.py new file mode 100644 index 00000000..a016a529 --- /dev/null +++ b/src/prelude_python_sdk/types/verification/phone/phone_verification_carrier.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ...._models import BaseModel + +__all__ = ["PhoneVerificationCarrier"] + + +class PhoneVerificationCarrier(BaseModel): + """The end user's mobile network.""" + + mccmnc: str + + name: Optional[str] = None diff --git a/src/prelude_python_sdk/types/verification/phone/phone_verification_money.py b/src/prelude_python_sdk/types/verification/phone/phone_verification_money.py new file mode 100644 index 00000000..ae6e5095 --- /dev/null +++ b/src/prelude_python_sdk/types/verification/phone/phone_verification_money.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ...._models import BaseModel + +__all__ = ["PhoneVerificationMoney"] + + +class PhoneVerificationMoney(BaseModel): + amount: str + """Exact decimal amount. + + It is never rounded to the currency's minor units, so a sub-cent cost reads as + `0.0004` rather than as `0.00`. + """ + + currency: str + """ISO 4217 currency code.""" diff --git a/src/prelude_python_sdk/types/verification/phone/phone_verification_psd2_transaction.py b/src/prelude_python_sdk/types/verification/phone/phone_verification_psd2_transaction.py new file mode 100644 index 00000000..a3f7d531 --- /dev/null +++ b/src/prelude_python_sdk/types/verification/phone/phone_verification_psd2_transaction.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ...._models import BaseModel +from .phone_verification_money import PhoneVerificationMoney + +__all__ = ["PhoneVerificationPsd2Transaction"] + + +class PhoneVerificationPsd2Transaction(BaseModel): + amount: Optional[PhoneVerificationMoney] = None + + recipient: Optional[str] = None + """Payee name displayed to the payer.""" diff --git a/src/prelude_python_sdk/types/verification_check_params.py b/src/prelude_python_sdk/types/verification_check_params.py index 139376e7..486b313e 100644 --- a/src/prelude_python_sdk/types/verification_check_params.py +++ b/src/prelude_python_sdk/types/verification_check_params.py @@ -2,9 +2,11 @@ from __future__ import annotations -from typing_extensions import Literal, Required, TypedDict +from typing_extensions import Required, TypedDict -__all__ = ["VerificationCheckParams", "Target", "Psd2"] +from .shared_params.target import Target + +__all__ = ["VerificationCheckParams", "Psd2"] class VerificationCheckParams(TypedDict, total=False): @@ -27,19 +29,6 @@ class VerificationCheckParams(TypedDict, total=False): """ -class Target(TypedDict, total=False): - """The verification target. - - Either a phone number or an email address. To use the email verification feature contact us to discuss your use case. - """ - - type: Required[Literal["phone_number", "email_address"]] - """The type of the target. Either "phone_number" or "email_address".""" - - value: Required[str] - """An E.164 formatted phone number or an email address.""" - - class Psd2(TypedDict, total=False): """Required when checking a code issued under the `prelude:psd2` template. diff --git a/src/prelude_python_sdk/types/verification_create_params.py b/src/prelude_python_sdk/types/verification_create_params.py index 73efc284..cc1bf07c 100644 --- a/src/prelude_python_sdk/types/verification_create_params.py +++ b/src/prelude_python_sdk/types/verification_create_params.py @@ -5,7 +5,10 @@ from typing import Dict, List from typing_extensions import Literal, Required, TypedDict -__all__ = ["VerificationCreateParams", "Target", "Metadata", "Options", "OptionsAppRealm", "Signals"] +from .shared_params.target import Target +from .shared_params.signals import Signals + +__all__ = ["VerificationCreateParams", "Metadata", "Options", "OptionsAppRealm"] class VerificationCreateParams(TypedDict, total=False): @@ -37,19 +40,6 @@ class VerificationCreateParams(TypedDict, total=False): """ -class Target(TypedDict, total=False): - """The verification target. - - Either a phone number or an email address. To use the email verification feature contact us to discuss your use case. - """ - - type: Required[Literal["phone_number", "email_address"]] - """The type of the target. Either "phone_number" or "email_address".""" - - value: Required[str] - """An E.164 formatted phone number or an email address.""" - - class Metadata(TypedDict, total=False): """The metadata for this verification. @@ -204,70 +194,3 @@ class Options(TypedDict, total=False): variables: Dict[str, str] """The variables to be replaced in the template.""" - - -class Signals(TypedDict, total=False): - """The signals used for anti-fraud. - - For more details, refer to [Signals](/verify/v2/documentation/prevent-fraud#signals). - """ - - app_version: str - """The version of your application.""" - - device_id: str - """A unique ID for the user's device. - - You should ensure that each user device has a unique `device_id` value. Ideally, - for Android, this corresponds to the `ANDROID_ID` and for iOS, this corresponds - to the `identifierForVendor`. - """ - - device_model: str - """The model of the user's device.""" - - device_platform: Literal["android", "ios", "ipados", "tvos", "web"] - """The type of the user's device.""" - - existing_user: bool - """ - Whether the end-user already exists in your system, for example an existing - account signing in again rather than a first-time signup. Unlike - `is_trusted_user`, this signal does not bypass fraud checks; it is taken into - account as one additional anti-fraud signal. For more details, refer to - [Signals](/verify/v2/documentation/prevent-fraud#signals). - """ - - ip: str - """The public IP v4 or v6 address of the end-user's device. - - You should collect this from your backend. If your backend is behind a proxy, - use the `X-Forwarded-For`, `Forwarded`, `True-Client-IP`, `CF-Connecting-IP` or - an equivalent header to get the actual public IP of the end-user's device. - """ - - is_trusted_user: bool - """ - This signal should indicate a higher level of trust, explicitly stating that the - user is genuine. Contact us to discuss your use case. For more details, refer to - [Signals](/verify/v2/documentation/prevent-fraud#signals). - """ - - ja4_fingerprint: str - """The JA4 fingerprint observed for the end-user's connection. - - Prelude will infer it automatically when you use our Frontend SDKs (which use - Prelude's edge network), but you can also forward the value if you terminate TLS - yourself. - """ - - os_version: str - """The version of the user's device operating system.""" - - user_agent: str - """The user agent of the user's device. - - If the individual fields (os_version, device_platform, device_model) are - provided, we will prioritize those values instead of parsing them from the user - agent string. - """ diff --git a/src/prelude_python_sdk/types/verification_management/__init__.py b/src/prelude_python_sdk/types/verification_management/__init__.py new file mode 100644 index 00000000..f67e8284 --- /dev/null +++ b/src/prelude_python_sdk/types/verification_management/__init__.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .sandbox_add_phone_number_params import SandboxAddPhoneNumberParams as SandboxAddPhoneNumberParams +from .sandbox_add_phone_number_response import SandboxAddPhoneNumberResponse as SandboxAddPhoneNumberResponse +from .sandbox_list_phone_numbers_response import SandboxListPhoneNumbersResponse as SandboxListPhoneNumbersResponse +from .sandbox_delete_phone_number_response import SandboxDeletePhoneNumberResponse as SandboxDeletePhoneNumberResponse diff --git a/src/prelude_python_sdk/types/verification_management/sandbox_add_phone_number_params.py b/src/prelude_python_sdk/types/verification_management/sandbox_add_phone_number_params.py new file mode 100644 index 00000000..daf5e7d0 --- /dev/null +++ b/src/prelude_python_sdk/types/verification_management/sandbox_add_phone_number_params.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["SandboxAddPhoneNumberParams"] + + +class SandboxAddPhoneNumberParams(TypedDict, total=False): + attempt_code: Required[str] + """ + The fixed attempt code that will validate verification attempts for this phone + number. + """ + + phone_number: Required[str] + """An E.164 formatted phone number to add to the sandbox list.""" diff --git a/src/prelude_python_sdk/types/verification_management/sandbox_add_phone_number_response.py b/src/prelude_python_sdk/types/verification_management/sandbox_add_phone_number_response.py new file mode 100644 index 00000000..d73ac1e3 --- /dev/null +++ b/src/prelude_python_sdk/types/verification_management/sandbox_add_phone_number_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["SandboxAddPhoneNumberResponse"] + + +class SandboxAddPhoneNumberResponse(BaseModel): + attempt_code: str + """The fixed attempt code associated with the sandbox phone number.""" + + phone_number: str + """The E.164 formatted phone number that was added to the sandbox list.""" diff --git a/src/prelude_python_sdk/types/verification_management/sandbox_delete_phone_number_response.py b/src/prelude_python_sdk/types/verification_management/sandbox_delete_phone_number_response.py new file mode 100644 index 00000000..f12c1bf1 --- /dev/null +++ b/src/prelude_python_sdk/types/verification_management/sandbox_delete_phone_number_response.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["SandboxDeletePhoneNumberResponse"] + + +class SandboxDeletePhoneNumberResponse(BaseModel): + phone_number: str + """The E.164 formatted phone number that was removed from the sandbox list.""" diff --git a/src/prelude_python_sdk/types/verification_management/sandbox_list_phone_numbers_response.py b/src/prelude_python_sdk/types/verification_management/sandbox_list_phone_numbers_response.py new file mode 100644 index 00000000..deed7f78 --- /dev/null +++ b/src/prelude_python_sdk/types/verification_management/sandbox_list_phone_numbers_response.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from datetime import datetime + +from ..._models import BaseModel + +__all__ = ["SandboxListPhoneNumbersResponse", "PhoneNumber"] + + +class PhoneNumber(BaseModel): + attempt_code: str + """The fixed attempt code associated with the sandbox phone number.""" + + created_at: datetime + """The date and time when the phone number was added to the sandbox list.""" + + phone_number: str + """An E.164 formatted phone number.""" + + +class SandboxListPhoneNumbersResponse(BaseModel): + phone_numbers: List[PhoneNumber] + """A list of sandbox phone numbers.""" diff --git a/src/prelude_python_sdk/types/watch_evaluate_params.py b/src/prelude_python_sdk/types/watch_evaluate_params.py new file mode 100644 index 00000000..1cbc9183 --- /dev/null +++ b/src/prelude_python_sdk/types/watch_evaluate_params.py @@ -0,0 +1,48 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import Required, TypedDict + +from .shared_params.target import Target +from .shared_params.signals import Signals + +__all__ = ["WatchEvaluateParams"] + + +class WatchEvaluateParams(TypedDict, total=False): + flow_id: Required[str] + """The flow to evaluate. + + A flow names the moment you are guarding and selects the recipes that run. + """ + + target: Required[Target] + """The identifier to score — a phone number or email address.""" + + attributes: Dict[str, str] + """ + Values for the attributes the flow's recipes declare, keyed without the `attr.` + namespace a rule uses to reference them. + + An attribute a recipe declares and this request omits is treated as missing + evidence, not as an empty value: the rules reading it report `NOT_EVALUATED` + rather than being scored as though the condition were false. A key no recipe in + the flow declares is ignored rather than rejected, so one payload can serve + flows that read different attributes. + """ + + dispatch_id: str + """The identifier of the dispatch that came from the front-end SDK. + + Signals it carries fill in anything the request did not state; the request wins + where both supply a value. + """ + + signals: Signals + """The signals used for anti-fraud. + + For more details, refer to + [Signals](/verify/v2/documentation/prevent-fraud#signals). + """ diff --git a/src/prelude_python_sdk/types/watch_evaluate_response.py b/src/prelude_python_sdk/types/watch_evaluate_response.py new file mode 100644 index 00000000..a9b6def5 --- /dev/null +++ b/src/prelude_python_sdk/types/watch_evaluate_response.py @@ -0,0 +1,125 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["WatchEvaluateResponse", "Recipe", "RecipeRule"] + + +class RecipeRule(BaseModel): + outcome: Literal["TRIGGERED", "NOT_TRIGGERED", "NOT_EVALUATED"] + """What the rule concluded. + + - `TRIGGERED` - The condition held; `weight` was added to the score. + - `NOT_TRIGGERED` - The condition did not hold. + - `NOT_EVALUATED` - The rule could not run, because something it reads never + arrived. This is not a quieter `NOT_TRIGGERED`: it contributed nothing either + way, and it is why `partial_evidence` is set on the recipe. + """ + + rule_id: str + """The rule that produced this result. + + Present whatever the rule's visibility, so a rule you cannot see the condition + of is still one you can reweight, switch off, or ask us about. + """ + + weight: int + """What this rule contributes to the recipe's score when it triggers.""" + + blocked_by: Optional[str] = None + """Why the rule could not run, set only when `outcome` is `NOT_EVALUATED`. + + A rule you authored names the signal or attribute it waited on, since you wrote + the expression that reads it. A Prelude-managed rule reports `missing_data` and + nothing more: the signal it waited on is part of a condition that is not + disclosed. + """ + + name: Optional[str] = None + """ + The rule's name, present for a rule you authored and omitted for a + Prelude-managed one. A managed rule's name describes what it looks for, which is + as much of the condition as the expression is. + """ + + unavailable: Optional[bool] = None + """ + The rule could not run for a reason on our side rather than anything about your + request. `outcome` is `NOT_EVALUATED` and the failure is ours to fix. + """ + + +class Recipe(BaseModel): + partial_evidence: bool + """ + At least one rule could not be evaluated, so the score rests on less than the + whole recipe. The score is still returned — a partial verdict is more useful + than none — but it is labeled rather than passed off as whole. + """ + + recipe_id: str + """The recipe that produced this result.""" + + rules: List[RecipeRule] + """One result per rule in the recipe, in membership order. + + Every rule runs — a score is only meaningful when complete, so there is no + short-circuit on the first trigger. + """ + + score: int + """ + The sum of the weights of the rules that triggered, clamped to the range -100 + to 100. Two scores at a bound are not comparable. + """ + + threshold: int + """The score at or above which this recipe flags.""" + + verdict: Literal["PASS", "FLAG"] + """This recipe's own verdict. + + Normally the score against the threshold, unless a preempting rule fired — see + `determined_by`. + """ + + determined_by: Optional[str] = None + """ + The preempting rule that set `verdict`, present only when a rule rather than the + score decided it. Without it a recipe can report a score under its threshold and + still flag, with nothing in the payload accounting for the difference. + """ + + +class WatchEvaluateResponse(BaseModel): + id: str + """The evaluation identifier.""" + + action: Literal["ALLOW", "BLOCK", "CHALLENGE"] + """ + What the evaluation suggests you do, being the most severe action across the + recipes that ran. Advisory: enforcement is yours. + + - `ALLOW` - Let the request through. + - `BLOCK` - Refuse the request. + - `CHALLENGE` - Let the request through behind an additional check. + """ + + recipes: List[Recipe] + """One result per recipe that ran. + + A recipe the flow names but that is not in service is absent rather than + reported as having passed. + """ + + verdict: Literal["PASS", "FLAG"] + """ + The evaluation-level verdict, being the most severe verdict across the recipes + that ran. + + - `PASS` - No recipe flagged. + - `FLAG` - At least one recipe flagged. + """ diff --git a/src/prelude_python_sdk/types/watch_predict_params.py b/src/prelude_python_sdk/types/watch_predict_params.py index 93a04966..a70ffdca 100644 --- a/src/prelude_python_sdk/types/watch_predict_params.py +++ b/src/prelude_python_sdk/types/watch_predict_params.py @@ -2,9 +2,12 @@ from __future__ import annotations -from typing_extensions import Literal, Required, TypedDict +from typing_extensions import Required, TypedDict -__all__ = ["WatchPredictParams", "Target", "Metadata", "Signals"] +from .shared_params.target import Target +from .shared_params.signals import Signals + +__all__ = ["WatchPredictParams", "Metadata"] class WatchPredictParams(TypedDict, total=False): @@ -25,16 +28,6 @@ class WatchPredictParams(TypedDict, total=False): """ -class Target(TypedDict, total=False): - """The signup identifier to score — a phone number or email address.""" - - type: Required[Literal["phone_number", "email_address"]] - """The type of the target. Either "phone_number" or "email_address".""" - - value: Required[str] - """An E.164 formatted phone number or an email address.""" - - class Metadata(TypedDict, total=False): """The metadata for this prediction.""" @@ -44,70 +37,3 @@ class Metadata(TypedDict, total=False): It is returned in the response and any webhook events that refer to this prediction. """ - - -class Signals(TypedDict, total=False): - """The signals used for anti-fraud. - - For more details, refer to [Signals](/verify/v2/documentation/prevent-fraud#signals). - """ - - app_version: str - """The version of your application.""" - - device_id: str - """A unique ID for the user's device. - - You should ensure that each user device has a unique `device_id` value. Ideally, - for Android, this corresponds to the `ANDROID_ID` and for iOS, this corresponds - to the `identifierForVendor`. - """ - - device_model: str - """The model of the user's device.""" - - device_platform: Literal["android", "ios", "ipados", "tvos", "web"] - """The type of the user's device.""" - - existing_user: bool - """ - Whether the end-user already exists in your system, for example an existing - account signing in again rather than a first-time signup. Unlike - `is_trusted_user`, this signal does not bypass fraud checks; it is taken into - account as one additional anti-fraud signal. For more details, refer to - [Signals](/verify/v2/documentation/prevent-fraud#signals). - """ - - ip: str - """The public IP v4 or v6 address of the end-user's device. - - You should collect this from your backend. If your backend is behind a proxy, - use the `X-Forwarded-For`, `Forwarded`, `True-Client-IP`, `CF-Connecting-IP` or - an equivalent header to get the actual public IP of the end-user's device. - """ - - is_trusted_user: bool - """ - This signal should indicate a higher level of trust, explicitly stating that the - user is genuine. Contact us to discuss your use case. For more details, refer to - [Signals](/verify/v2/documentation/prevent-fraud#signals). - """ - - ja4_fingerprint: str - """The JA4 fingerprint observed for the end-user's connection. - - Prelude will infer it automatically when you use our Frontend SDKs (which use - Prelude's edge network), but you can also forward the value if you terminate TLS - yourself. - """ - - os_version: str - """The version of the user's device operating system.""" - - user_agent: str - """The user agent of the user's device. - - If the individual fields (os_version, device_platform, device_model) are - provided, we will prioritize those values instead of parsing them from the user - agent string. - """ diff --git a/src/prelude_python_sdk/types/watch_send_events_params.py b/src/prelude_python_sdk/types/watch_send_events_params.py index 29d73caa..4adc34a2 100644 --- a/src/prelude_python_sdk/types/watch_send_events_params.py +++ b/src/prelude_python_sdk/types/watch_send_events_params.py @@ -5,7 +5,9 @@ from typing import Iterable from typing_extensions import Literal, Required, TypedDict -__all__ = ["WatchSendEventsParams", "Event", "EventTarget"] +from .shared_params.target import Target + +__all__ = ["WatchSendEventsParams", "Event"] class WatchSendEventsParams(TypedDict, total=False): @@ -16,16 +18,6 @@ class WatchSendEventsParams(TypedDict, total=False): """ -class EventTarget(TypedDict, total=False): - """The event target. Only supports phone numbers for now.""" - - type: Required[Literal["phone_number", "email_address"]] - """The type of the target. Either "phone_number" or "email_address".""" - - value: Required[str] - """An E.164 formatted phone number or an email address.""" - - class Event(TypedDict, total=False): confidence: Required[Literal["maximum", "high", "neutral", "low", "minimum"]] """ @@ -43,5 +35,5 @@ class Event(TypedDict, total=False): label: Required[str] """A label to describe what the event refers to.""" - target: Required[EventTarget] + target: Required[Target] """The event target. Only supports phone numbers for now.""" diff --git a/src/prelude_python_sdk/types/watch_send_feedbacks_params.py b/src/prelude_python_sdk/types/watch_send_feedbacks_params.py index 5eb13414..90f08af8 100644 --- a/src/prelude_python_sdk/types/watch_send_feedbacks_params.py +++ b/src/prelude_python_sdk/types/watch_send_feedbacks_params.py @@ -5,7 +5,9 @@ from typing import Iterable from typing_extensions import Literal, Required, TypedDict -__all__ = ["WatchSendFeedbacksParams", "Feedback", "FeedbackTarget", "FeedbackMetadata"] +from .shared_params.target import Target + +__all__ = ["WatchSendFeedbacksParams", "Feedback", "FeedbackMetadata"] class WatchSendFeedbacksParams(TypedDict, total=False): @@ -16,16 +18,6 @@ class WatchSendFeedbacksParams(TypedDict, total=False): """ -class FeedbackTarget(TypedDict, total=False): - """The feedback target. Only supports phone numbers for now.""" - - type: Required[Literal["phone_number", "email_address"]] - """The type of the target. Either "phone_number" or "email_address".""" - - value: Required[str] - """An E.164 formatted phone number or an email address.""" - - class FeedbackMetadata(TypedDict, total=False): """The metadata for this feedback.""" @@ -38,7 +30,7 @@ class FeedbackMetadata(TypedDict, total=False): class Feedback(TypedDict, total=False): - target: Required[FeedbackTarget] + target: Required[Target] """The feedback target. Only supports phone numbers for now.""" type: Required[Literal["verification.started", "verification.completed"]] diff --git a/tests/api_resources/intel/__init__.py b/tests/api_resources/intel/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/intel/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/intel/test_kyc.py b/tests/api_resources/intel/test_kyc.py new file mode 100644 index 00000000..88f4dc71 --- /dev/null +++ b/tests/api_resources/intel/test_kyc.py @@ -0,0 +1,133 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from tests.utils import assert_matches_type +from prelude_python_sdk import Prelude, AsyncPrelude +from prelude_python_sdk._utils import parse_date +from prelude_python_sdk.types.intel import KYCMatchResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestKYC: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_match(self, client: Prelude) -> None: + kyc = client.intel.kyc.match( + phone="+12065550100", + ) + assert_matches_type(KYCMatchResponse, kyc, path=["response"]) + + @parametrize + def test_method_match_with_all_params(self, client: Prelude) -> None: + kyc = client.intel.kyc.match( + phone="+12065550100", + address="12 rue de la Paix", + birthdate=parse_date("1990-01-15"), + country="FR", + email="jean.dupont@example.com", + family_name="Dupont", + given_name="Jean", + locality="Paris", + postal_code="75002", + region="Île-de-France", + ) + assert_matches_type(KYCMatchResponse, kyc, path=["response"]) + + @parametrize + def test_raw_response_match(self, client: Prelude) -> None: + response = client.intel.kyc.with_raw_response.match( + phone="+12065550100", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + kyc = response.parse() + assert_matches_type(KYCMatchResponse, kyc, path=["response"]) + + @parametrize + def test_streaming_response_match(self, client: Prelude) -> None: + with client.intel.kyc.with_streaming_response.match( + phone="+12065550100", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + kyc = response.parse() + assert_matches_type(KYCMatchResponse, kyc, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_match(self, client: Prelude) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `phone` but received ''"): + client.intel.kyc.with_raw_response.match( + phone="", + ) + + +class TestAsyncKYC: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_match(self, async_client: AsyncPrelude) -> None: + kyc = await async_client.intel.kyc.match( + phone="+12065550100", + ) + assert_matches_type(KYCMatchResponse, kyc, path=["response"]) + + @parametrize + async def test_method_match_with_all_params(self, async_client: AsyncPrelude) -> None: + kyc = await async_client.intel.kyc.match( + phone="+12065550100", + address="12 rue de la Paix", + birthdate=parse_date("1990-01-15"), + country="FR", + email="jean.dupont@example.com", + family_name="Dupont", + given_name="Jean", + locality="Paris", + postal_code="75002", + region="Île-de-France", + ) + assert_matches_type(KYCMatchResponse, kyc, path=["response"]) + + @parametrize + async def test_raw_response_match(self, async_client: AsyncPrelude) -> None: + response = await async_client.intel.kyc.with_raw_response.match( + phone="+12065550100", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + kyc = await response.parse() + assert_matches_type(KYCMatchResponse, kyc, path=["response"]) + + @parametrize + async def test_streaming_response_match(self, async_client: AsyncPrelude) -> None: + async with async_client.intel.kyc.with_streaming_response.match( + phone="+12065550100", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + kyc = await response.parse() + assert_matches_type(KYCMatchResponse, kyc, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_match(self, async_client: AsyncPrelude) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `phone` but received ''"): + await async_client.intel.kyc.with_raw_response.match( + phone="", + ) diff --git a/tests/api_resources/test_notify.py b/tests/api_resources/test_notify.py index db32ff7c..5b14f041 100644 --- a/tests/api_resources/test_notify.py +++ b/tests/api_resources/test_notify.py @@ -11,6 +11,7 @@ from prelude_python_sdk import Prelude, AsyncPrelude from prelude_python_sdk.types import ( NotifySendResponse, + NotifyReplyResponse, NotifySendBatchResponse, NotifyGetSubscriptionConfigResponse, NotifyListSubscriptionConfigsResponse, @@ -251,6 +252,54 @@ def test_path_params_list_subscription_phone_numbers(self, client: Prelude) -> N config_id="", ) + @parametrize + def test_method_reply(self, client: Prelude) -> None: + notify = client.notify.reply( + reply_to="im_01k8aq2zggeyssvt53zgvpx63a", + text="Thanks for reaching out! We'll look into your request.", + to="+33612345678", + ) + assert_matches_type(NotifyReplyResponse, notify, path=["response"]) + + @parametrize + def test_method_reply_with_all_params(self, client: Prelude) -> None: + notify = client.notify.reply( + reply_to="im_01k8aq2zggeyssvt53zgvpx63a", + text="Thanks for reaching out! We'll look into your request.", + to="+33612345678", + callback_url="https://your-app.com/webhooks/notify", + correlation_id="support-ticket-42", + ) + assert_matches_type(NotifyReplyResponse, notify, path=["response"]) + + @parametrize + def test_raw_response_reply(self, client: Prelude) -> None: + response = client.notify.with_raw_response.reply( + reply_to="im_01k8aq2zggeyssvt53zgvpx63a", + text="Thanks for reaching out! We'll look into your request.", + to="+33612345678", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + notify = response.parse() + assert_matches_type(NotifyReplyResponse, notify, path=["response"]) + + @parametrize + def test_streaming_response_reply(self, client: Prelude) -> None: + with client.notify.with_streaming_response.reply( + reply_to="im_01k8aq2zggeyssvt53zgvpx63a", + text="Thanks for reaching out! We'll look into your request.", + to="+33612345678", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + notify = response.parse() + assert_matches_type(NotifyReplyResponse, notify, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_send(self, client: Prelude) -> None: notify = client.notify.send( @@ -600,6 +649,54 @@ async def test_path_params_list_subscription_phone_numbers(self, async_client: A config_id="", ) + @parametrize + async def test_method_reply(self, async_client: AsyncPrelude) -> None: + notify = await async_client.notify.reply( + reply_to="im_01k8aq2zggeyssvt53zgvpx63a", + text="Thanks for reaching out! We'll look into your request.", + to="+33612345678", + ) + assert_matches_type(NotifyReplyResponse, notify, path=["response"]) + + @parametrize + async def test_method_reply_with_all_params(self, async_client: AsyncPrelude) -> None: + notify = await async_client.notify.reply( + reply_to="im_01k8aq2zggeyssvt53zgvpx63a", + text="Thanks for reaching out! We'll look into your request.", + to="+33612345678", + callback_url="https://your-app.com/webhooks/notify", + correlation_id="support-ticket-42", + ) + assert_matches_type(NotifyReplyResponse, notify, path=["response"]) + + @parametrize + async def test_raw_response_reply(self, async_client: AsyncPrelude) -> None: + response = await async_client.notify.with_raw_response.reply( + reply_to="im_01k8aq2zggeyssvt53zgvpx63a", + text="Thanks for reaching out! We'll look into your request.", + to="+33612345678", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + notify = await response.parse() + assert_matches_type(NotifyReplyResponse, notify, path=["response"]) + + @parametrize + async def test_streaming_response_reply(self, async_client: AsyncPrelude) -> None: + async with async_client.notify.with_streaming_response.reply( + reply_to="im_01k8aq2zggeyssvt53zgvpx63a", + text="Thanks for reaching out! We'll look into your request.", + to="+33612345678", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + notify = await response.parse() + assert_matches_type(NotifyReplyResponse, notify, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_send(self, async_client: AsyncPrelude) -> None: notify = await async_client.notify.send( diff --git a/tests/api_resources/test_watch.py b/tests/api_resources/test_watch.py index 95343e77..60d12510 100644 --- a/tests/api_resources/test_watch.py +++ b/tests/api_resources/test_watch.py @@ -11,6 +11,7 @@ from prelude_python_sdk import Prelude, AsyncPrelude from prelude_python_sdk.types import ( WatchPredictResponse, + WatchEvaluateResponse, WatchSendEventsResponse, WatchSendFeedbacksResponse, ) @@ -21,6 +22,77 @@ class TestWatch: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @parametrize + def test_method_evaluate(self, client: Prelude) -> None: + watch = client.watch.evaluate( + flow_id="flo_01jc0t6fwwfgfsq1md24mhyztj", + target={ + "type": "phone_number", + "value": "+30123456789", + }, + ) + assert_matches_type(WatchEvaluateResponse, watch, path=["response"]) + + @parametrize + def test_method_evaluate_with_all_params(self, client: Prelude) -> None: + watch = client.watch.evaluate( + flow_id="flo_01jc0t6fwwfgfsq1md24mhyztj", + target={ + "type": "phone_number", + "value": "+30123456789", + }, + attributes={ + "plan_tier": "free", + "account_age_days": "3", + }, + dispatch_id="123e4567-e89b-12d3-a456-426614174000", + signals={ + "app_version": "1.2.34", + "device_id": "8F0B8FDD-C2CB-4387-B20A-56E9B2E5A0D2", + "device_model": "iPhone17,2", + "device_platform": "ios", + "existing_user": False, + "ip": "203.0.113.123", + "is_trusted_user": False, + "ja4_fingerprint": "t13d1516h2_8daaf6152771_e5627efa2ab1", + "os_version": "18.0.1", + "user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Mobile/15E148 Safari/604.1", + }, + ) + assert_matches_type(WatchEvaluateResponse, watch, path=["response"]) + + @parametrize + def test_raw_response_evaluate(self, client: Prelude) -> None: + response = client.watch.with_raw_response.evaluate( + flow_id="flo_01jc0t6fwwfgfsq1md24mhyztj", + target={ + "type": "phone_number", + "value": "+30123456789", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + watch = response.parse() + assert_matches_type(WatchEvaluateResponse, watch, path=["response"]) + + @parametrize + def test_streaming_response_evaluate(self, client: Prelude) -> None: + with client.watch.with_streaming_response.evaluate( + flow_id="flo_01jc0t6fwwfgfsq1md24mhyztj", + target={ + "type": "phone_number", + "value": "+30123456789", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + watch = response.parse() + assert_matches_type(WatchEvaluateResponse, watch, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_predict(self, client: Prelude) -> None: watch = client.watch.predict( @@ -204,6 +276,77 @@ class TestAsyncWatch: "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) + @parametrize + async def test_method_evaluate(self, async_client: AsyncPrelude) -> None: + watch = await async_client.watch.evaluate( + flow_id="flo_01jc0t6fwwfgfsq1md24mhyztj", + target={ + "type": "phone_number", + "value": "+30123456789", + }, + ) + assert_matches_type(WatchEvaluateResponse, watch, path=["response"]) + + @parametrize + async def test_method_evaluate_with_all_params(self, async_client: AsyncPrelude) -> None: + watch = await async_client.watch.evaluate( + flow_id="flo_01jc0t6fwwfgfsq1md24mhyztj", + target={ + "type": "phone_number", + "value": "+30123456789", + }, + attributes={ + "plan_tier": "free", + "account_age_days": "3", + }, + dispatch_id="123e4567-e89b-12d3-a456-426614174000", + signals={ + "app_version": "1.2.34", + "device_id": "8F0B8FDD-C2CB-4387-B20A-56E9B2E5A0D2", + "device_model": "iPhone17,2", + "device_platform": "ios", + "existing_user": False, + "ip": "203.0.113.123", + "is_trusted_user": False, + "ja4_fingerprint": "t13d1516h2_8daaf6152771_e5627efa2ab1", + "os_version": "18.0.1", + "user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Mobile/15E148 Safari/604.1", + }, + ) + assert_matches_type(WatchEvaluateResponse, watch, path=["response"]) + + @parametrize + async def test_raw_response_evaluate(self, async_client: AsyncPrelude) -> None: + response = await async_client.watch.with_raw_response.evaluate( + flow_id="flo_01jc0t6fwwfgfsq1md24mhyztj", + target={ + "type": "phone_number", + "value": "+30123456789", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + watch = await response.parse() + assert_matches_type(WatchEvaluateResponse, watch, path=["response"]) + + @parametrize + async def test_streaming_response_evaluate(self, async_client: AsyncPrelude) -> None: + async with async_client.watch.with_streaming_response.evaluate( + flow_id="flo_01jc0t6fwwfgfsq1md24mhyztj", + target={ + "type": "phone_number", + "value": "+30123456789", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + watch = await response.parse() + assert_matches_type(WatchEvaluateResponse, watch, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_predict(self, async_client: AsyncPrelude) -> None: watch = await async_client.watch.predict( diff --git a/tests/api_resources/verification/__init__.py b/tests/api_resources/verification/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/verification/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/verification/phone/__init__.py b/tests/api_resources/verification/phone/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/verification/phone/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/verification/phone/test_history.py b/tests/api_resources/verification/phone/test_history.py new file mode 100644 index 00000000..705402fd --- /dev/null +++ b/tests/api_resources/verification/phone/test_history.py @@ -0,0 +1,190 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from tests.utils import assert_matches_type +from prelude_python_sdk import Prelude, AsyncPrelude +from prelude_python_sdk._utils import parse_datetime +from prelude_python_sdk.types.verification.phone import ( + HistoryListResponse, + HistoryRetrieveResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestHistory: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_retrieve(self, client: Prelude) -> None: + history = client.verification.phone.history.retrieve( + "vrf_01jc0t6fwwfgfsq1md24mhyztj", + ) + assert_matches_type(HistoryRetrieveResponse, history, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: Prelude) -> None: + response = client.verification.phone.history.with_raw_response.retrieve( + "vrf_01jc0t6fwwfgfsq1md24mhyztj", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + history = response.parse() + assert_matches_type(HistoryRetrieveResponse, history, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: Prelude) -> None: + with client.verification.phone.history.with_streaming_response.retrieve( + "vrf_01jc0t6fwwfgfsq1md24mhyztj", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + history = response.parse() + assert_matches_type(HistoryRetrieveResponse, history, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: Prelude) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.verification.phone.history.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_list(self, client: Prelude) -> None: + history = client.verification.phone.history.list() + assert_matches_type(HistoryListResponse, history, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: Prelude) -> None: + history = client.verification.phone.history.list( + channels=["sms"], + cursor="cursor", + device_platform="android", + from_=parse_datetime("2026-09-01T00:00:00Z"), + limit=1, + max_attempts=0, + min_attempts=0, + phone_number="+33612345678", + region="FR", + status="converted", + template_id="template_01jc0t6fwwfgfsq1md24mhyztj", + to=parse_datetime("2026-09-08T00:00:00Z"), + ) + assert_matches_type(HistoryListResponse, history, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: Prelude) -> None: + response = client.verification.phone.history.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + history = response.parse() + assert_matches_type(HistoryListResponse, history, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: Prelude) -> None: + with client.verification.phone.history.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + history = response.parse() + assert_matches_type(HistoryListResponse, history, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncHistory: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncPrelude) -> None: + history = await async_client.verification.phone.history.retrieve( + "vrf_01jc0t6fwwfgfsq1md24mhyztj", + ) + assert_matches_type(HistoryRetrieveResponse, history, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncPrelude) -> None: + response = await async_client.verification.phone.history.with_raw_response.retrieve( + "vrf_01jc0t6fwwfgfsq1md24mhyztj", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + history = await response.parse() + assert_matches_type(HistoryRetrieveResponse, history, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncPrelude) -> None: + async with async_client.verification.phone.history.with_streaming_response.retrieve( + "vrf_01jc0t6fwwfgfsq1md24mhyztj", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + history = await response.parse() + assert_matches_type(HistoryRetrieveResponse, history, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncPrelude) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.verification.phone.history.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncPrelude) -> None: + history = await async_client.verification.phone.history.list() + assert_matches_type(HistoryListResponse, history, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncPrelude) -> None: + history = await async_client.verification.phone.history.list( + channels=["sms"], + cursor="cursor", + device_platform="android", + from_=parse_datetime("2026-09-01T00:00:00Z"), + limit=1, + max_attempts=0, + min_attempts=0, + phone_number="+33612345678", + region="FR", + status="converted", + template_id="template_01jc0t6fwwfgfsq1md24mhyztj", + to=parse_datetime("2026-09-08T00:00:00Z"), + ) + assert_matches_type(HistoryListResponse, history, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncPrelude) -> None: + response = await async_client.verification.phone.history.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + history = await response.parse() + assert_matches_type(HistoryListResponse, history, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncPrelude) -> None: + async with async_client.verification.phone.history.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + history = await response.parse() + assert_matches_type(HistoryListResponse, history, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/verification_management/__init__.py b/tests/api_resources/verification_management/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/verification_management/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/verification_management/test_sandbox.py b/tests/api_resources/verification_management/test_sandbox.py new file mode 100644 index 00000000..36eae1cc --- /dev/null +++ b/tests/api_resources/verification_management/test_sandbox.py @@ -0,0 +1,224 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from tests.utils import assert_matches_type +from prelude_python_sdk import Prelude, AsyncPrelude +from prelude_python_sdk.types.verification_management import ( + SandboxAddPhoneNumberResponse, + SandboxListPhoneNumbersResponse, + SandboxDeletePhoneNumberResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestSandbox: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_add_phone_number(self, client: Prelude) -> None: + sandbox = client.verification_management.sandbox.add_phone_number( + attempt_code="123456", + phone_number="+30123456789", + ) + assert_matches_type(SandboxAddPhoneNumberResponse, sandbox, path=["response"]) + + @parametrize + def test_raw_response_add_phone_number(self, client: Prelude) -> None: + response = client.verification_management.sandbox.with_raw_response.add_phone_number( + attempt_code="123456", + phone_number="+30123456789", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + sandbox = response.parse() + assert_matches_type(SandboxAddPhoneNumberResponse, sandbox, path=["response"]) + + @parametrize + def test_streaming_response_add_phone_number(self, client: Prelude) -> None: + with client.verification_management.sandbox.with_streaming_response.add_phone_number( + attempt_code="123456", + phone_number="+30123456789", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + sandbox = response.parse() + assert_matches_type(SandboxAddPhoneNumberResponse, sandbox, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_delete_phone_number(self, client: Prelude) -> None: + sandbox = client.verification_management.sandbox.delete_phone_number( + "+12065550100", + ) + assert_matches_type(SandboxDeletePhoneNumberResponse, sandbox, path=["response"]) + + @parametrize + def test_raw_response_delete_phone_number(self, client: Prelude) -> None: + response = client.verification_management.sandbox.with_raw_response.delete_phone_number( + "+12065550100", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + sandbox = response.parse() + assert_matches_type(SandboxDeletePhoneNumberResponse, sandbox, path=["response"]) + + @parametrize + def test_streaming_response_delete_phone_number(self, client: Prelude) -> None: + with client.verification_management.sandbox.with_streaming_response.delete_phone_number( + "+12065550100", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + sandbox = response.parse() + assert_matches_type(SandboxDeletePhoneNumberResponse, sandbox, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete_phone_number(self, client: Prelude) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `phone_number` but received ''"): + client.verification_management.sandbox.with_raw_response.delete_phone_number( + "", + ) + + @parametrize + def test_method_list_phone_numbers(self, client: Prelude) -> None: + sandbox = client.verification_management.sandbox.list_phone_numbers() + assert_matches_type(SandboxListPhoneNumbersResponse, sandbox, path=["response"]) + + @parametrize + def test_raw_response_list_phone_numbers(self, client: Prelude) -> None: + response = client.verification_management.sandbox.with_raw_response.list_phone_numbers() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + sandbox = response.parse() + assert_matches_type(SandboxListPhoneNumbersResponse, sandbox, path=["response"]) + + @parametrize + def test_streaming_response_list_phone_numbers(self, client: Prelude) -> None: + with client.verification_management.sandbox.with_streaming_response.list_phone_numbers() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + sandbox = response.parse() + assert_matches_type(SandboxListPhoneNumbersResponse, sandbox, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncSandbox: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_add_phone_number(self, async_client: AsyncPrelude) -> None: + sandbox = await async_client.verification_management.sandbox.add_phone_number( + attempt_code="123456", + phone_number="+30123456789", + ) + assert_matches_type(SandboxAddPhoneNumberResponse, sandbox, path=["response"]) + + @parametrize + async def test_raw_response_add_phone_number(self, async_client: AsyncPrelude) -> None: + response = await async_client.verification_management.sandbox.with_raw_response.add_phone_number( + attempt_code="123456", + phone_number="+30123456789", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + sandbox = await response.parse() + assert_matches_type(SandboxAddPhoneNumberResponse, sandbox, path=["response"]) + + @parametrize + async def test_streaming_response_add_phone_number(self, async_client: AsyncPrelude) -> None: + async with async_client.verification_management.sandbox.with_streaming_response.add_phone_number( + attempt_code="123456", + phone_number="+30123456789", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + sandbox = await response.parse() + assert_matches_type(SandboxAddPhoneNumberResponse, sandbox, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_delete_phone_number(self, async_client: AsyncPrelude) -> None: + sandbox = await async_client.verification_management.sandbox.delete_phone_number( + "+12065550100", + ) + assert_matches_type(SandboxDeletePhoneNumberResponse, sandbox, path=["response"]) + + @parametrize + async def test_raw_response_delete_phone_number(self, async_client: AsyncPrelude) -> None: + response = await async_client.verification_management.sandbox.with_raw_response.delete_phone_number( + "+12065550100", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + sandbox = await response.parse() + assert_matches_type(SandboxDeletePhoneNumberResponse, sandbox, path=["response"]) + + @parametrize + async def test_streaming_response_delete_phone_number(self, async_client: AsyncPrelude) -> None: + async with async_client.verification_management.sandbox.with_streaming_response.delete_phone_number( + "+12065550100", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + sandbox = await response.parse() + assert_matches_type(SandboxDeletePhoneNumberResponse, sandbox, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete_phone_number(self, async_client: AsyncPrelude) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `phone_number` but received ''"): + await async_client.verification_management.sandbox.with_raw_response.delete_phone_number( + "", + ) + + @parametrize + async def test_method_list_phone_numbers(self, async_client: AsyncPrelude) -> None: + sandbox = await async_client.verification_management.sandbox.list_phone_numbers() + assert_matches_type(SandboxListPhoneNumbersResponse, sandbox, path=["response"]) + + @parametrize + async def test_raw_response_list_phone_numbers(self, async_client: AsyncPrelude) -> None: + response = await async_client.verification_management.sandbox.with_raw_response.list_phone_numbers() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + sandbox = await response.parse() + assert_matches_type(SandboxListPhoneNumbersResponse, sandbox, path=["response"]) + + @parametrize + async def test_streaming_response_list_phone_numbers(self, async_client: AsyncPrelude) -> None: + async with ( + async_client.verification_management.sandbox.with_streaming_response.list_phone_numbers() + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + sandbox = await response.parse() + assert_matches_type(SandboxListPhoneNumbersResponse, sandbox, path=["response"]) + + assert cast(Any, response.is_closed) is True From 8d20fd9f2a7fcceb722a0c15fd42eef7035036f9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 18 Sep 2026 15:48:50 +0000 Subject: [PATCH 2/2] release: 0.15.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/prelude_python_sdk/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index a26ebfc1..8f3e0a49 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.14.0" + ".": "0.15.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index d4ff5333..04acfe8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.15.0 (2026-09-18) + +Full Changelog: [v0.14.0...v0.15.0](https://github.com/prelude-so/python-sdk/compare/v0.14.0...v0.15.0) + +### Features + +* **api:** manual updates ([6f8fe45](https://github.com/prelude-so/python-sdk/commit/6f8fe45cb794e6283855863693c12143ec2b4871)) + ## 0.14.0 (2026-09-15) Full Changelog: [v0.13.0...v0.14.0](https://github.com/prelude-so/python-sdk/compare/v0.13.0...v0.14.0) diff --git a/pyproject.toml b/pyproject.toml index 2bdc9df6..090aafbf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "prelude-python-sdk" -version = "0.14.0" +version = "0.15.0" description = "The official Python library for the Prelude API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/prelude_python_sdk/_version.py b/src/prelude_python_sdk/_version.py index 2aab5a9c..888f88cf 100644 --- a/src/prelude_python_sdk/_version.py +++ b/src/prelude_python_sdk/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "prelude_python_sdk" -__version__ = "0.14.0" # x-release-please-version +__version__ = "0.15.0" # x-release-please-version