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
8 changes: 4 additions & 4 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": "1b0c7bdd8d7957bdf314651ebd7ecc491457810dd7a1430bce84feac9caea305",
"fetched_at": "2026-11-06T06:33:19Z"
"sha256": "40264da5f817f42878488bf1ed0cdd46ecb4d1f3c98fae158904c4dcf76e4b85",
"fetched_at": "2026-11-07T06:29:49Z"
},
"account.json": {
"url": "https://docs.didit.me/openapi-auth.json",
"sha256": "ef403603543b119fed4300d08e15b8ec0a13de9fabdfdc02866f61f817d2a1f0",
"fetched_at": "2026-11-06T06:33:19Z"
"fetched_at": "2026-11-07T06:29:49Z"
},
"webhook-vectors.json": {
"generator": "product-automations scripts/server-sdks/make_vectors.py (seeded 2026-10-17)",
Expand All @@ -16,6 +16,6 @@
"authentication.json": {
"url": "https://docs.didit.me/openapi-authentication.json",
"sha256": "3987984647bfcaa4b5462c8d60cd3c53894bf7585a38109e0cc813292b2416fe",
"fetched_at": "2026-11-06T12:17:36Z"
"fetched_at": "2026-11-07T06:29:49Z"
}
}
67 changes: 34 additions & 33 deletions openapi/verification.json

Large diffs are not rendered by default.

12 changes: 4 additions & 8 deletions src/didit/resources/bank.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,6 @@ def account_check(
) -> BankAccountCheckResponse:
"""Check a bank account.

**Not available in production yet.** Bank Verification is switched off in production, so until it is switched on this endpoint answers `404` with `bank_verification_not_available`. Do not build on it yet. The rest of this description is what the endpoint does once it is available.

Check that a bank account belongs to the person or company you name, before you send money to it. No session and no sign-in: send the name and the account, read the result. It is the [account check](/core-technology/bank-verification/account-check) of the Bank Verification step, run from your backend.

**What you send.** The account's `country` (ISO 3166-1 alpha-3), the `account` the way that country writes it, and the `holder` name: `full_name` for a person or `business_name` for a company. A business name checks a business account.
Expand All @@ -73,14 +71,14 @@ def account_check(

**Every request is a new check.** There is no idempotency key and no endpoint to read a check back, so a retried request runs, and is billed, again. Store the response.

**Sandbox.** Where Bank Verification is available, a sandbox key calls no bank and is charged nothing. The last 4 characters of the IBAN, else of the account number, choose the answer: `0001` a `close_match` with a `holder_name`, `0002` a `close_match` without one, `0003` and `0006` a `no_match`, `0004` `cannot_verify`, `0005` a `503` and `0009` a `400`. Any other ending is a `match`.
**Sandbox.** Use a sandbox API key with the sample account details. A sandbox key calls no bank and is charged nothing. The last 4 characters of the IBAN, else of the account number, choose the answer: `0001` a `close_match` with a `holder_name`, `0002` a `close_match` without one, `0003` and `0006` a `no_match`, `0004` `cannot_verify`, `0005` a `503` and `0009` a `400`. Any other ending is a `match`.

**API keys.** A scoped API key needs write access to sessions.

``POST /v3/bank/account-check/``

Args:
country: The account's country, as an ISO 3166-1 alpha-3 code, for example `GBR`. Lowercase is accepted.
country: The account's country, as an ISO 3166-1 alpha-3 code, for example `NGA`. Lowercase is accepted.
account: The account the way its country writes it: an `iban`, or an `account_number` with the bank or branch code the country needs (`sort_code`, `routing_number`, `transit_institution`, `bsb`, `ifsc`, `bank_code` or `bic`). Mexico takes a `clabe` and Argentina a `cbu_cvu`, which already name the bank. A business account adds a `registration_id` where the country asks for one. Spaces, dashes and dots are ignored. Keys the country's check does not use are ignored; a key it needs and does not get is a `400` that names it.
holder: The name the account is checked against. Send exactly one of `full_name` and `business_name`.
vendor_data: Your own reference for this check, returned unchanged.
Expand Down Expand Up @@ -143,8 +141,6 @@ async def account_check(
) -> BankAccountCheckResponse:
"""Check a bank account.

**Not available in production yet.** Bank Verification is switched off in production, so until it is switched on this endpoint answers `404` with `bank_verification_not_available`. Do not build on it yet. The rest of this description is what the endpoint does once it is available.

Check that a bank account belongs to the person or company you name, before you send money to it. No session and no sign-in: send the name and the account, read the result. It is the [account check](/core-technology/bank-verification/account-check) of the Bank Verification step, run from your backend.

**What you send.** The account's `country` (ISO 3166-1 alpha-3), the `account` the way that country writes it, and the `holder` name: `full_name` for a person or `business_name` for a company. A business name checks a business account.
Expand All @@ -155,14 +151,14 @@ async def account_check(

**Every request is a new check.** There is no idempotency key and no endpoint to read a check back, so a retried request runs, and is billed, again. Store the response.

**Sandbox.** Where Bank Verification is available, a sandbox key calls no bank and is charged nothing. The last 4 characters of the IBAN, else of the account number, choose the answer: `0001` a `close_match` with a `holder_name`, `0002` a `close_match` without one, `0003` and `0006` a `no_match`, `0004` `cannot_verify`, `0005` a `503` and `0009` a `400`. Any other ending is a `match`.
**Sandbox.** Use a sandbox API key with the sample account details. A sandbox key calls no bank and is charged nothing. The last 4 characters of the IBAN, else of the account number, choose the answer: `0001` a `close_match` with a `holder_name`, `0002` a `close_match` without one, `0003` and `0006` a `no_match`, `0004` `cannot_verify`, `0005` a `503` and `0009` a `400`. Any other ending is a `match`.

**API keys.** A scoped API key needs write access to sessions.

``POST /v3/bank/account-check/``

Args:
country: The account's country, as an ISO 3166-1 alpha-3 code, for example `GBR`. Lowercase is accepted.
country: The account's country, as an ISO 3166-1 alpha-3 code, for example `NGA`. Lowercase is accepted.
account: The account the way its country writes it: an `iban`, or an `account_number` with the bank or branch code the country needs (`sort_code`, `routing_number`, `transit_institution`, `bsb`, `ifsc`, `bank_code` or `bic`). Mexico takes a `clabe` and Argentina a `cbu_cvu`, which already name the bank. A business account adds a `registration_id` where the country asks for one. Spaces, dashes and dots are ignored. Keys the country's check does not use are ignored; a key it needs and does not get is a `400` that names it.
holder: The name the account is checked against. Send exactly one of `full_name` and `business_name`.
vendor_data: Your own reference for this check, returned unchanged.
Expand Down
4 changes: 2 additions & 2 deletions src/didit/resources/email.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ def send_code(

**How send and check pair up.** Verification state is keyed by your application plus the `email` address - the check call does not take `request_id`. Each new send creates a pending verification that lives for **5 minutes**: call the check with the same address and the code the user received before the window closes. Calling send again for the same address while a verification is pending generates a fresh code for that same verification (`status: "Retry"`, same `request_id`); the previous code stops working. At most one retry is attached this way (two sends total); a further send starts a fresh verification with a new `request_id`. The 5-minute window is measured from the first send and is not extended by retries.

**Deliverability pre-check.** Before anything is sent, the address goes through syntax and DNS/MX validation. Addresses that cannot receive mail return `200` with `status: "Undeliverable"` and `reason: "email_can_not_be_delivered"` - no email is sent, nothing is billed, and the verification is immediately finalized as `Declined` (a follow-up check returns `Expired or Not Found`). A downstream send failure reports the same way.
**Deliverability pre-check.** Before anything is sent, the address goes through syntax and DNS/MX validation. Addresses that cannot receive mail return `200` with `status: "Undeliverable"` and `reason: "email_can_not_be_delivered"` - no email is sent, nothing is billed, and the verification is immediately finalized as `Declined` (a follow-up check returns `Expired or Not Found`). A send that fails for a reason that is not the address (the email could not be handed over for delivery) returns `503` instead: no code is sent, nothing is billed, the verification stays open and the same request can be repeated.

**Code format and branding.** Codes are 4–8 characters (`options.code_size`, default 6), numeric by default; set `options.alphanumeric_code: true` for uppercase letters and digits (the check comparison is case-insensitive). The email template is localized via `options.locale` (54 supported languages) and can use your application's white-label branding via `options.use_white_label_customization`.

Expand Down Expand Up @@ -383,7 +383,7 @@ async def send_code(

**How send and check pair up.** Verification state is keyed by your application plus the `email` address - the check call does not take `request_id`. Each new send creates a pending verification that lives for **5 minutes**: call the check with the same address and the code the user received before the window closes. Calling send again for the same address while a verification is pending generates a fresh code for that same verification (`status: "Retry"`, same `request_id`); the previous code stops working. At most one retry is attached this way (two sends total); a further send starts a fresh verification with a new `request_id`. The 5-minute window is measured from the first send and is not extended by retries.

**Deliverability pre-check.** Before anything is sent, the address goes through syntax and DNS/MX validation. Addresses that cannot receive mail return `200` with `status: "Undeliverable"` and `reason: "email_can_not_be_delivered"` - no email is sent, nothing is billed, and the verification is immediately finalized as `Declined` (a follow-up check returns `Expired or Not Found`). A downstream send failure reports the same way.
**Deliverability pre-check.** Before anything is sent, the address goes through syntax and DNS/MX validation. Addresses that cannot receive mail return `200` with `status: "Undeliverable"` and `reason: "email_can_not_be_delivered"` - no email is sent, nothing is billed, and the verification is immediately finalized as `Declined` (a follow-up check returns `Expired or Not Found`). A send that fails for a reason that is not the address (the email could not be handed over for delivery) returns `503` instead: no code is sent, nothing is billed, the verification stays open and the same request can be repeated.

**Code format and branding.** Codes are 4–8 characters (`options.code_size`, default 6), numeric by default; set `options.alphanumeric_code: true` for uppercase letters and digits (the check comparison is case-insensitive). The email template is localized via `options.locale` (54 supported languages) and can use your application's white-label branding via `options.use_white_label_customization`.

Expand Down
4 changes: 2 additions & 2 deletions src/didit/resources/pricing.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def bank_verification(
) -> 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. Bank Verification is not enabled in production yet, so a bank step is refused on production workflows until it is; treat these prices as a list price, not as an offer you can buy today. Use it to quote a bank step before you build a workflow, or to show bank prices in your own product.
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. A method a country does not offer today shows as not available, with no price. 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.

Expand Down Expand Up @@ -111,7 +111,7 @@ async def bank_verification(
) -> 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. Bank Verification is not enabled in production yet, so a bank step is refused on production workflows until it is; treat these prices as a list price, not as an offer you can buy today. Use it to quote a bank step before you build a workflow, or to show bank prices in your own product.
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. A method a country does not offer today shows as not available, with no price. 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.

Expand Down
2 changes: 1 addition & 1 deletion src/didit/types/bank/account_check_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class BankAccountCheckParamsAccount(TypedDict, total=False):

class BankAccountCheckParams(TypedDict, total=False):
country: Required[str]
"""The account's country, as an ISO 3166-1 alpha-3 code, for example `GBR`. Lowercase is accepted."""
"""The account's country, as an ISO 3166-1 alpha-3 code, for example `NGA`. Lowercase is accepted."""

account: Required[BankAccountCheckParamsAccount]
"""The account the way its country writes it: an `iban`, or an `account_number` with the bank or branch code the country needs (`sort_code`, `routing_number`, `transit_institution`, `bsb`, `ifsc`, `bank_code` or `bic`). Mexico takes a `clabe` and Argentina a `cbu_cvu`, which already name the bank. A business account adds a `registration_id` where the country asks for one. Spaces, dashes and dots are ignored. Keys the country's check does not use are ignored; a key it needs and does not get is a `400` that names it."""
Expand Down
2 changes: 1 addition & 1 deletion src/didit/types/components/session_decision.py
Original file line number Diff line number Diff line change
Expand Up @@ -3965,7 +3965,7 @@ class SessionDecision(BaseModel):
"""**Array of questionnaire response reports** - always a JSON array, never a singular `questionnaire` object. `null` until at least one questionnaire step has produced data. Each item is the full report, including `node_id`, the questionnaire metadata, sections with form elements, and the user's answers."""

bank_verifications: list[BankVerificationResult] | None = None
"""Bank Verification results, one item per Bank Verification step, for User and Business Verification. `null` until a step produces one. Each item's `method` says how the account was verified: `open_banking` (bank sign-in) or `account_lookup` (account check), or a `statement` or `microdeposits` fallback, and `none` when the person continued without a bank before any attempt was evaluated (`continued_without_bank`). See [Bank verification methods](/core-technology/bank-verification/verification-methods). Bank Verification is not enabled in production yet, so this stays `null` on production sessions until it is."""
"""Bank Verification results, one item per Bank Verification step, for User and Business Verification. `null` until a step produces one. Each item's `method` says how the account was verified: `open_banking` (bank sign-in) or `account_lookup` (account check), or a `statement` or `microdeposits` fallback, and `none` when the person continued without a bank before any attempt was evaluated (`continued_without_bank`). See [Bank verification methods](/core-technology/bank-verification/verification-methods)."""

signatures: list[SignatureResult] | None = None
"""E-signature results, one item per signer and signature step, for User and Business Verification. Present only when the workflow has an E-signature step; an empty array until a signer reaches it. See the [E-signature report](/core-technology/electronic-signature/report-electronic-signature)."""
Expand Down
14 changes: 7 additions & 7 deletions tests/generated/test_bank.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ def test_bank_account_check() -> None:
api="verification",
auth="api_key",
json_body={
"country": "GBR",
"account": {"sort_code": "20-45-67", "account_number": "31926819"},
"country": "NGA",
"account": {"bank_code": "000014", "account_number": "0123450001"},
"holder": {"full_name": "Maria Oliveira"},
"vendor_data": "payout_20481",
},
Expand All @@ -26,20 +26,20 @@ def test_bank_account_check() -> None:
"request_id": "c2f4a9e0-7b1d-4c3a-9e55-1d0a6b8f2a47",
"result": "close_match",
"holder_name": "MARIA R OLIVEIRA",
"account": "****6819",
"account": "****0001",
"vendor_data": "payout_20481",
"created_at": "2026-10-21T14:32:08Z",
},
),
lambda c: c.bank.account_check(
country="GBR",
account={"sort_code": "20-45-67", "account_number": "31926819"},
country="NGA",
account={"bank_code": "000014", "account_number": "0123450001"},
holder={"full_name": "Maria Oliveira"},
vendor_data="payout_20481",
),
lambda c: c.bank.account_check(
country="GBR",
account={"sort_code": "20-45-67", "account_number": "31926819"},
country="NGA",
account={"bank_code": "000014", "account_number": "0123450001"},
holder={"full_name": "Maria Oliveira"},
vendor_data="payout_20481",
),
Expand Down
Loading