Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ client.cases.list() # application scoped, see Configuration
client.account.login(email="you@example.com", password="...")
```

The resource tree covers sessions (with imports and reviews), the standalone APIs (`id_verification`, `face_match`, `face_search`, `passive_liveness`, `age_estimation`, `proof_of_address`, `document_ai`, `aml`, `database_validation`, `phone`, `email`, `ip`, `kyb`), `id_wallets`, `fraud_checks`, `users`, `businesses`, `transactions`, `wallet_screening`, `travel_rule`, `workflows`, `questionnaires`, `webhooks.destinations`, `billing`, `biometric_templates`, `customization`, `cases`, `regulatory_reports`, `report_templates`, `networks` and `account`. The API reference at [docs.didit.me](https://docs.didit.me) documents every endpoint; each SDK method's docstring names the endpoint it calls.
The resource tree covers sessions (with imports and reviews), the standalone APIs (`id_verification`, `face_match`, `face_search`, `passive_liveness`, `age_estimation`, `proof_of_address`, `document_ai`, `aml`, `database_validation`, `phone`, `email`, `ip`, `kyb`), `id_wallets`, `fraud_checks`, `users`, `businesses`, `transactions`, `wallet_screening`, `travel_rule`, `workflows`, `questionnaires`, `webhooks.destinations`, `billing`, `pricing`, `biometric_templates`, `customization`, `cases`, `regulatory_reports`, `report_templates`, `networks` and `account`. The API reference at [docs.didit.me](https://docs.didit.me) documents every endpoint; each SDK method's docstring names the endpoint it calls.

Request and response types live in `didit.types`, named after the method (`SessionsCreateResponse`, `SessionsCreateParams`) or after the API schema (`TransactionDetail`). Responses are [pydantic](https://docs.pydantic.dev) models:

Expand Down
8 changes: 7 additions & 1 deletion codegen/didit_codegen/render_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ def conforms(spec: Spec, schema: Json, value: Json, *, request: bool, depth: int


_SECRET_KEY = re.compile(r"token|secret|api_?key|password|private_?key|credential", re.IGNORECASE)
_TIMESTAMP = re.compile(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})")
_JWT = re.compile(r"^eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]*$")


Expand All @@ -227,7 +228,12 @@ def scrub(value: Json, key: str | None = None) -> Json:
if isinstance(value, str):
if _JWT.match(value):
return "sample-token"
if key is not None and _SECRET_KEY.search(key) and value:
if (
key is not None
and _SECRET_KEY.search(key)
and value
and not _TIMESTAMP.fullmatch(value)
):
return "sample-" + ("token" if "token" in key.lower() else "secret")
return value

Expand Down
21 changes: 21 additions & 0 deletions codegen/tests/test_codegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -621,3 +621,24 @@ def test_cli_main_from_dir(
assert spec_sync.main(["--from-dir", str(openapi), "--summary", str(summary)]) == 0
assert "changed=false" in output.read_text("utf-8")
assert summary.read_text("utf-8") == "No upstream change.\n"


def test_scrub_keeps_timestamps_and_replaces_secrets() -> None:
from didit_codegen.render_tests import scrub

got = scrub(
{
"credentials_expires_at": "2026-10-27T10:00:00Z",
"api_key": "abc",
"password": "2026-10-27-secret",
"token_date": "2026-10-27",
"api_key_at": "2026-10-27T10:00",
}
)
assert got == {
"credentials_expires_at": "2026-10-27T10:00:00Z",
"api_key": "sample-secret",
"password": "sample-secret",
"token_date": "sample-token",
"api_key_at": "sample-secret",
}
6 changes: 3 additions & 3 deletions openapi/SOURCE.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"verification.json": {
"url": "https://docs.didit.me/openapi-25.json",
"sha256": "6157d174fff4e0093ca7ac8e0f064ffc777d1cfa4a7eeb2b0e88fbf86f4a060b",
"fetched_at": "2026-10-26T07:02:07Z"
"sha256": "b2658eb22e23f868d2df5fe513c8dcdadce3abf2e35d1bbebce0382c911038db",
"fetched_at": "2026-10-27T06:49:57Z"
},
"account.json": {
"url": "https://docs.didit.me/openapi-auth.json",
"sha256": "5fafb88b306ab60c7c105b0bf00fa2a6a49b5d2ca8115eb003b332f073844b39",
"fetched_at": "2026-10-26T07:02:07Z"
"fetched_at": "2026-10-27T06:49:57Z"
},
"webhook-vectors.json": {
"generator": "product-automations scripts/server-sdks/make_vectors.py (seeded 2026-10-17)",
Expand Down
1,921 changes: 1,731 additions & 190 deletions openapi/verification.json

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions src/didit/resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
from didit.resources.networks import AsyncNetworks, Networks
from didit.resources.passive_liveness import AsyncPassiveLiveness, PassiveLiveness
from didit.resources.phone import AsyncPhone, Phone
from didit.resources.pricing import AsyncPricing, Pricing
from didit.resources.proof_of_address import AsyncProofOfAddress, ProofOfAddress
from didit.resources.questionnaires import AsyncQuestionnaires, Questionnaires
from didit.resources.regulatory_reports import AsyncRegulatoryReports, RegulatoryReports
Expand Down Expand Up @@ -230,6 +231,12 @@ def phone(self) -> Phone:

return Phone(self)

@cached_property
def pricing(self) -> Pricing:
from didit.resources.pricing import Pricing

return Pricing(self)

@cached_property
def proof_of_address(self) -> ProofOfAddress:
from didit.resources.proof_of_address import ProofOfAddress
Expand Down Expand Up @@ -474,6 +481,12 @@ def phone(self) -> AsyncPhone:

return AsyncPhone(self)

@cached_property
def pricing(self) -> AsyncPricing:
from didit.resources.pricing import AsyncPricing

return AsyncPricing(self)

@cached_property
def proof_of_address(self) -> AsyncProofOfAddress:
from didit.resources.proof_of_address import AsyncProofOfAddress
Expand Down
166 changes: 166 additions & 0 deletions src/didit/resources/pricing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
# Code generated by codegen from openapi/*.json. DO NOT EDIT.
"""The ``pricing`` resource."""

from __future__ import annotations

from functools import cached_property
from typing import cast

import httpx

from didit._resource import AsyncAPIResource, SyncAPIResource
from didit._response import (
async_to_raw_response_wrapper,
async_to_streamed_response_wrapper,
to_raw_response_wrapper,
to_streamed_response_wrapper,
)
from didit._types import NOT_GIVEN, Body, Headers, NotGiven, Query
from didit.types.components.bank_verification_pricing import BankVerificationPricing

__all__ = [
"Pricing",
"AsyncPricing",
"PricingWithRawResponse",
"AsyncPricingWithRawResponse",
"PricingWithStreamingResponse",
"AsyncPricingWithStreamingResponse",
]


class Pricing(SyncAPIResource):
"""The ``pricing`` resource of the Didit API.

Methods: ``bank_verification``.
"""

@cached_property
def with_raw_response(self) -> PricingWithRawResponse:
"""Every method of this resource, returning the raw HTTP response."""
return PricingWithRawResponse(self)

@cached_property
def with_streaming_response(self) -> PricingWithStreamingResponse:
"""Every method of this resource, returning the HTTP response before reading its body."""
return PricingWithStreamingResponse(self)

def bank_verification(
self,
*,
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
) -> BankVerificationPricing:
"""Get bank verification prices (public, unauthenticated).

Returns the list price of each Bank Verification method in every country: bank sign-in with its balance and transactions add-ons, and the account check. Use it to quote a bank step before you build a workflow, or to show bank prices in your own product.

Public: send no auth header. The response contains no organization data and is the same for every caller. It is cached publicly for one hour and carries an `ETag`, so send `If-None-Match` to get a `304 Not Modified` when nothing changed.

A method is `available` only where Didit serves it in production today, and its `price_usd` is `null` everywhere else, so the feed never shows a price you cannot buy. `available: false` covers a country with no approved price, a method the country does not support, and a route that is not switched on yet.

These are list prices before your plan discount. Your plan discount applies to the whole bank price, and a negotiated or contract price replaces the list price for your organization.

``GET /v3/organization/bank-verification-pricing/``

Args:
extra_headers: Headers to add to (or, with ``None``, remove from) this request.
extra_query: Query parameters to add to this request.
extra_body: Top-level JSON fields to add to this request's body.
timeout: Overrides the client's timeout for this request, in seconds.
"""
return cast(
"BankVerificationPricing",
self._client.request(
"get",
"/v3/organization/bank-verification-pricing/",
cast_to=BankVerificationPricing,
auth="none",
extra_headers=extra_headers,
extra_query=extra_query,
extra_body=extra_body,
timeout=timeout,
),
)


class AsyncPricing(AsyncAPIResource):
"""The ``pricing`` resource of the Didit API.

Methods: ``bank_verification``.
"""

@cached_property
def with_raw_response(self) -> AsyncPricingWithRawResponse:
"""Every method of this resource, returning the raw HTTP response."""
return AsyncPricingWithRawResponse(self)

@cached_property
def with_streaming_response(self) -> AsyncPricingWithStreamingResponse:
"""Every method of this resource, returning the HTTP response before reading its body."""
return AsyncPricingWithStreamingResponse(self)

async def bank_verification(
self,
*,
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
) -> BankVerificationPricing:
"""Get bank verification prices (public, unauthenticated).

Returns the list price of each Bank Verification method in every country: bank sign-in with its balance and transactions add-ons, and the account check. Use it to quote a bank step before you build a workflow, or to show bank prices in your own product.

Public: send no auth header. The response contains no organization data and is the same for every caller. It is cached publicly for one hour and carries an `ETag`, so send `If-None-Match` to get a `304 Not Modified` when nothing changed.

A method is `available` only where Didit serves it in production today, and its `price_usd` is `null` everywhere else, so the feed never shows a price you cannot buy. `available: false` covers a country with no approved price, a method the country does not support, and a route that is not switched on yet.

These are list prices before your plan discount. Your plan discount applies to the whole bank price, and a negotiated or contract price replaces the list price for your organization.

``GET /v3/organization/bank-verification-pricing/``

Args:
extra_headers: Headers to add to (or, with ``None``, remove from) this request.
extra_query: Query parameters to add to this request.
extra_body: Top-level JSON fields to add to this request's body.
timeout: Overrides the client's timeout for this request, in seconds.
"""
return cast(
"BankVerificationPricing",
await self._client.request(
"get",
"/v3/organization/bank-verification-pricing/",
cast_to=BankVerificationPricing,
auth="none",
extra_headers=extra_headers,
extra_query=extra_query,
extra_body=extra_body,
timeout=timeout,
),
)


class PricingWithRawResponse:
def __init__(self, resource: Pricing) -> None:
self._resource = resource
self.bank_verification = to_raw_response_wrapper(resource.bank_verification)


class AsyncPricingWithRawResponse:
def __init__(self, resource: AsyncPricing) -> None:
self._resource = resource
self.bank_verification = async_to_raw_response_wrapper(resource.bank_verification)


class PricingWithStreamingResponse:
def __init__(self, resource: Pricing) -> None:
self._resource = resource
self.bank_verification = to_streamed_response_wrapper(resource.bank_verification)


class AsyncPricingWithStreamingResponse:
def __init__(self, resource: AsyncPricing) -> None:
self._resource = resource
self.bank_verification = async_to_streamed_response_wrapper(resource.bank_verification)
Loading
Loading