diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index d61779f3..d3dc9f51 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.114.0"
+ ".": "0.115.0"
}
\ No newline at end of file
diff --git a/.stats.yml b/.stats.yml
index d9f35dd3..14cb5bb0 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 175
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/lithic%2Flithic-ce2adff9b644ed4562b5342a4a43d0b40c98d43b4e063b4626f4ca5d342f1b92.yml
-openapi_spec_hash: fbc84b866ce96457261ac58b4e75c71d
-config_hash: 31d71922d7838f34ae0875c9b8026d99
+configured_endpoints: 176
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/lithic%2Flithic-f137c504db2dbc70382232b1db7ed592a852bc58068a4e72d5ba7ce724110b3d.yml
+openapi_spec_hash: 20abc9c95ee5814da6d7aeccec75e996
+config_hash: 693dddc4721eef512d75ab6c60897794
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f5f83006..235e0cc6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,37 @@
# Changelog
+## 0.115.0 (2026-02-12)
+
+Full Changelog: [v0.114.0...v0.115.0](https://github.com/lithic-com/lithic-python/compare/v0.114.0...v0.115.0)
+
+### Features
+
+* **api:** Add /v2/auth_rules/results endpoint for listing rule evaluation data ([aea1658](https://github.com/lithic-com/lithic-python/commit/aea1658f300aeb41d859c9bb7271d883f3b4a2c3))
+* **api:** Add hold token field to book transfers ([51d053e](https://github.com/lithic-com/lithic-python/commit/51d053eb788f43396d71c14acfd6dd80e8fefe77))
+* **api:** Add naics_code to account holder requests/responses ([a7135fb](https://github.com/lithic-com/lithic-python/commit/a7135fb5f63687973cc1856731eb6c7f524994e3))
+* **api:** Add PENDING_REVIEW status to KYB enrollment simulation ([ede8a8a](https://github.com/lithic-com/lithic-python/commit/ede8a8ac24af85f8dd21178007af35f7b35aa133))
+* **api:** Add result schemas for Authorization and Authentication (3DS) actions ([ced5f0d](https://github.com/lithic-com/lithic-python/commit/ced5f0db1ae27e02bde4402cfff8ef4a9e33ce1a))
+* **client:** add custom JSON encoder for extended type support ([5658dcc](https://github.com/lithic-com/lithic-python/commit/5658dccf4d989063d2d4ffdb243cfec17861a000))
+
+
+### Bug Fixes
+
+* **api:** Update /v2/auth_rules/results endpoint parameter naming and action types ([e50dd4d](https://github.com/lithic-com/lithic-python/commit/e50dd4d8a866a47d77c0e92c6e0703053c54f403))
+
+
+### Chores
+
+* configure new SDK language ([292317d](https://github.com/lithic-com/lithic-python/commit/292317d003f007992d8848fbe4a870164bac9d29))
+* Enable stainless MCP in config ([a799eec](https://github.com/lithic-com/lithic-python/commit/a799eeca71933e98243cf351caf374b9a59d9ce9))
+* format all `api.md` files ([f428d56](https://github.com/lithic-com/lithic-python/commit/f428d56ec1200a79ea23658daf01771dc8b198c7))
+* **internal:** bump dependencies ([67243a9](https://github.com/lithic-com/lithic-python/commit/67243a9d839bb33d4b53a7a7b8cc02678d068f66))
+* **internal:** fix lint error on Python 3.14 ([73de2f2](https://github.com/lithic-com/lithic-python/commit/73de2f20082fecd5e07af008ff79b161c2b4378d))
+
+
+### Documentation
+
+* Fix documentation of tokenization channel and tokenization source for tokenization rules ([7d111f8](https://github.com/lithic-com/lithic-python/commit/7d111f8f8ee283412f9846f239ff626a410a2a24))
+
## 0.114.0 (2026-01-27)
Full Changelog: [v0.113.0...v0.114.0](https://github.com/lithic-com/lithic-python/compare/v0.113.0...v0.114.0)
diff --git a/README.md b/README.md
index 0aaa73df..d91fcf39 100644
--- a/README.md
+++ b/README.md
@@ -7,6 +7,15 @@ The Lithic Python library provides convenient access to the Lithic REST API from
application. The library includes type definitions for all request params and response fields,
and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx).
+## MCP Server
+
+Use the Lithic MCP Server to enable AI assistants to interact with this API, allowing them to explore endpoints, make test requests, and use documentation to help integrate this SDK into your application.
+
+[](https://cursor.com/en-US/install-mcp?name=lithic-mcp&config=eyJuYW1lIjoibGl0aGljLW1jcCIsInRyYW5zcG9ydCI6Imh0dHAiLCJ1cmwiOiJodHRwczovL2xpdGhpYy5zdGxtY3AuY29tIiwiaGVhZGVycyI6eyJ4LWxpdGhpYy1hcGkta2V5IjoiTXkgTGl0aGljIEFQSSBLZXkifX0)
+[](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22lithic-mcp%22%2C%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Flithic.stlmcp.com%22%2C%22headers%22%3A%7B%22x-lithic-api-key%22%3A%22My%20Lithic%20API%20Key%22%7D%7D)
+
+> Note: You may need to set environment variables in your MCP client.
+
## Documentation
The REST API documentation can be found on [docs.lithic.com](https://docs.lithic.com). The full API of this library can be found in [api.md](api.md).
diff --git a/api.md b/api.md
index a4790892..92414f8a 100644
--- a/api.md
+++ b/api.md
@@ -96,6 +96,7 @@ from lithic.types.auth_rules import (
RuleStats,
VelocityLimitParams,
VelocityLimitPeriod,
+ V2ListResultsResponse,
V2RetrieveFeaturesResponse,
V2RetrieveReportResponse,
)
@@ -109,6 +110,7 @@ Methods:
- client.auth_rules.v2.list(\*\*params) -> SyncCursorPage[AuthRule]
- client.auth_rules.v2.delete(auth_rule_token) -> None
- client.auth_rules.v2.draft(auth_rule_token, \*\*params) -> AuthRule
+- client.auth_rules.v2.list_results(\*\*params) -> SyncCursorPage[V2ListResultsResponse]
- client.auth_rules.v2.promote(auth_rule_token) -> AuthRule
- client.auth_rules.v2.retrieve_features(auth_rule_token, \*\*params) -> V2RetrieveFeaturesResponse
- client.auth_rules.v2.retrieve_report(auth_rule_token, \*\*params) -> V2RetrieveReportResponse
diff --git a/pyproject.toml b/pyproject.toml
index 4c357454..5f325713 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "lithic"
-version = "0.114.0"
+version = "0.115.0"
description = "The official Python library for the lithic API"
dynamic = ["readme"]
license = "Apache-2.0"
@@ -71,7 +71,7 @@ format = { chain = [
# run formatting again to fix any inconsistencies when imports are stripped
"format:ruff",
]}
-"format:docs" = "python scripts/utils/ruffen-docs.py README.md api.md"
+"format:docs" = "bash -c 'python scripts/utils/ruffen-docs.py README.md $(find . -type f -name api.md)'"
"format:ruff" = "ruff format"
"lint" = { chain = [
diff --git a/requirements-dev.lock b/requirements-dev.lock
index 83c0dadf..197f41b6 100644
--- a/requirements-dev.lock
+++ b/requirements-dev.lock
@@ -12,14 +12,14 @@
-e file:.
aiohappyeyeballs==2.6.1
# via aiohttp
-aiohttp==3.13.2
+aiohttp==3.13.3
# via httpx-aiohttp
# via lithic
aiosignal==1.4.0
# via aiohttp
annotated-types==0.7.0
# via pydantic
-anyio==4.12.0
+anyio==4.12.1
# via httpx
# via lithic
argcomplete==3.6.3
@@ -32,7 +32,7 @@ attrs==25.4.0
# via standardwebhooks
backports-asyncio-runner==1.2.0
# via pytest-asyncio
-certifi==2025.11.12
+certifi==2026.1.4
# via httpcore
# via httpx
colorlog==6.10.1
@@ -65,7 +65,7 @@ httpx==0.28.1
# via lithic
# via respx
# via standardwebhooks
-httpx-aiohttp==0.1.9
+httpx-aiohttp==0.1.12
# via lithic
humanize==4.13.0
# via nox
@@ -73,7 +73,7 @@ idna==3.11
# via anyio
# via httpx
# via yarl
-importlib-metadata==8.7.0
+importlib-metadata==8.7.1
iniconfig==2.1.0
# via pytest
markdown-it-py==3.0.0
@@ -86,14 +86,14 @@ multidict==6.7.0
mypy==1.17.0
mypy-extensions==1.1.0
# via mypy
-nodeenv==1.9.1
+nodeenv==1.10.0
# via pyright
nox==2025.11.12
packaging==25.0
# via dependency-groups
# via nox
# via pytest
-pathspec==0.12.1
+pathspec==1.0.3
# via mypy
platformdirs==4.4.0
# via virtualenv
@@ -120,7 +120,7 @@ python-dateutil==2.9.0.post0
# via time-machine
respx==0.22.0
rich==14.2.0
-ruff==0.14.7
+ruff==0.14.13
six==1.17.0
# via python-dateutil
sniffio==1.3.1
@@ -128,7 +128,7 @@ sniffio==1.3.1
standardwebhooks==1.0.0
# via lithic
time-machine==2.19.0
-tomli==2.3.0
+tomli==2.4.0
# via dependency-groups
# via mypy
# via nox
@@ -152,7 +152,7 @@ typing-extensions==4.15.0
# via virtualenv
typing-inspection==0.4.2
# via pydantic
-virtualenv==20.35.4
+virtualenv==20.36.1
# via nox
wrapt==2.0.1
# via deprecated
diff --git a/requirements.lock b/requirements.lock
index 08c68c21..01e8660f 100644
--- a/requirements.lock
+++ b/requirements.lock
@@ -12,14 +12,14 @@
-e file:.
aiohappyeyeballs==2.6.1
# via aiohttp
-aiohttp==3.13.2
+aiohttp==3.13.3
# via httpx-aiohttp
# via lithic
aiosignal==1.4.0
# via aiohttp
annotated-types==0.7.0
# via pydantic
-anyio==4.12.0
+anyio==4.12.1
# via httpx
# via lithic
async-timeout==5.0.1
@@ -27,7 +27,7 @@ async-timeout==5.0.1
attrs==25.4.0
# via aiohttp
# via standardwebhooks
-certifi==2025.11.12
+certifi==2026.1.4
# via httpcore
# via httpx
deprecated==1.3.1
@@ -47,7 +47,7 @@ httpx==0.28.1
# via httpx-aiohttp
# via lithic
# via standardwebhooks
-httpx-aiohttp==0.1.9
+httpx-aiohttp==0.1.12
# via lithic
idna==3.11
# via anyio
diff --git a/src/lithic/_base_client.py b/src/lithic/_base_client.py
index 73cedbe6..e992a795 100644
--- a/src/lithic/_base_client.py
+++ b/src/lithic/_base_client.py
@@ -86,6 +86,7 @@
APIConnectionError,
APIResponseValidationError,
)
+from ._utils._json import openapi_dumps
from ._legacy_response import LegacyAPIResponse
log: logging.Logger = logging.getLogger(__name__)
@@ -555,8 +556,10 @@ def _build_request(
kwargs["content"] = options.content
elif isinstance(json_data, bytes):
kwargs["content"] = json_data
- else:
- kwargs["json"] = json_data if is_given(json_data) else None
+ elif not files:
+ # Don't set content when JSON is sent as multipart/form-data,
+ # since httpx's content param overrides other body arguments
+ kwargs["content"] = openapi_dumps(json_data) if is_given(json_data) and json_data is not None else None
kwargs["files"] = files
else:
headers.pop("Content-Type", None)
diff --git a/src/lithic/_compat.py b/src/lithic/_compat.py
index bdef67f0..786ff42a 100644
--- a/src/lithic/_compat.py
+++ b/src/lithic/_compat.py
@@ -139,6 +139,7 @@ def model_dump(
exclude_defaults: bool = False,
warnings: bool = True,
mode: Literal["json", "python"] = "python",
+ by_alias: bool | None = None,
) -> dict[str, Any]:
if (not PYDANTIC_V1) or hasattr(model, "model_dump"):
return model.model_dump(
@@ -148,13 +149,12 @@ def model_dump(
exclude_defaults=exclude_defaults,
# warnings are not supported in Pydantic v1
warnings=True if PYDANTIC_V1 else warnings,
+ by_alias=by_alias,
)
return cast(
"dict[str, Any]",
model.dict( # pyright: ignore[reportDeprecated, reportUnnecessaryCast]
- exclude=exclude,
- exclude_unset=exclude_unset,
- exclude_defaults=exclude_defaults,
+ exclude=exclude, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, by_alias=bool(by_alias)
),
)
diff --git a/src/lithic/_utils/_compat.py b/src/lithic/_utils/_compat.py
index dd703233..2c70b299 100644
--- a/src/lithic/_utils/_compat.py
+++ b/src/lithic/_utils/_compat.py
@@ -26,7 +26,7 @@ def is_union(tp: Optional[Type[Any]]) -> bool:
else:
import types
- return tp is Union or tp is types.UnionType
+ return tp is Union or tp is types.UnionType # type: ignore[comparison-overlap]
def is_typeddict(tp: Type[Any]) -> bool:
diff --git a/src/lithic/_utils/_json.py b/src/lithic/_utils/_json.py
new file mode 100644
index 00000000..60584214
--- /dev/null
+++ b/src/lithic/_utils/_json.py
@@ -0,0 +1,35 @@
+import json
+from typing import Any
+from datetime import datetime
+from typing_extensions import override
+
+import pydantic
+
+from .._compat import model_dump
+
+
+def openapi_dumps(obj: Any) -> bytes:
+ """
+ Serialize an object to UTF-8 encoded JSON bytes.
+
+ Extends the standard json.dumps with support for additional types
+ commonly used in the SDK, such as `datetime`, `pydantic.BaseModel`, etc.
+ """
+ return json.dumps(
+ obj,
+ cls=_CustomEncoder,
+ # Uses the same defaults as httpx's JSON serialization
+ ensure_ascii=False,
+ separators=(",", ":"),
+ allow_nan=False,
+ ).encode()
+
+
+class _CustomEncoder(json.JSONEncoder):
+ @override
+ def default(self, o: Any) -> Any:
+ if isinstance(o, datetime):
+ return o.isoformat()
+ if isinstance(o, pydantic.BaseModel):
+ return model_dump(o, exclude_unset=True, mode="json", by_alias=True)
+ return super().default(o)
diff --git a/src/lithic/_version.py b/src/lithic/_version.py
index 9e8952a6..9b480c33 100644
--- a/src/lithic/_version.py
+++ b/src/lithic/_version.py
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
__title__ = "lithic"
-__version__ = "0.114.0" # x-release-please-version
+__version__ = "0.115.0" # x-release-please-version
diff --git a/src/lithic/resources/account_holders.py b/src/lithic/resources/account_holders.py
index bd371f9b..b4440f72 100644
--- a/src/lithic/resources/account_holders.py
+++ b/src/lithic/resources/account_holders.py
@@ -70,6 +70,7 @@ def create(
beneficial_owner_entities: Iterable[account_holder_create_params.KYBBeneficialOwnerEntity] | Omit = omit,
external_id: str | Omit = omit,
kyb_passed_timestamp: str | Omit = omit,
+ naics_code: str | Omit = omit,
website_url: 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.
@@ -127,6 +128,9 @@ def create(
This field is required only if workflow type is `KYB_BYO`.
+ naics_code: 6-digit North American Industry Classification System (NAICS) code for the
+ business.
+
website_url: Company website URL.
extra_headers: Send extra headers
@@ -148,6 +152,7 @@ def create(
| Omit = omit,
control_person: account_holder_create_params.KYBDelegatedControlPerson | Omit = omit,
external_id: str | Omit = omit,
+ naics_code: str | Omit = omit,
nature_of_business: str | Omit = omit,
tos_timestamp: str | Omit = omit,
website_url: str | Omit = omit,
@@ -191,6 +196,9 @@ def create(
external_id: A user provided id that can be used to link an account holder with an external
system
+ naics_code: 6-digit North American Industry Classification System (NAICS) code for the
+ business.
+
nature_of_business: Short description of the company's line of business (i.e., what does the company
do?).
@@ -364,6 +372,7 @@ def create(
beneficial_owner_entities: Iterable[account_holder_create_params.KYBBeneficialOwnerEntity] | Omit = omit,
external_id: str | Omit = omit,
kyb_passed_timestamp: str | Omit = omit,
+ naics_code: str | Omit = omit,
website_url: str | Omit = omit,
individual: account_holder_create_params.KYCIndividual | Omit = omit,
kyc_passed_timestamp: str | Omit = omit,
@@ -396,6 +405,7 @@ def create(
"beneficial_owner_entities": beneficial_owner_entities,
"external_id": external_id,
"kyb_passed_timestamp": kyb_passed_timestamp,
+ "naics_code": naics_code,
"website_url": website_url,
"individual": individual,
"kyc_passed_timestamp": kyc_passed_timestamp,
@@ -463,6 +473,7 @@ def update(
business_entity: account_holder_update_params.KYBPatchRequestBusinessEntity | Omit = omit,
control_person: account_holder_update_params.KYBPatchRequestControlPerson | Omit = omit,
external_id: str | Omit = omit,
+ naics_code: str | Omit = omit,
nature_of_business: str | Omit = omit,
website_url: str | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -509,6 +520,9 @@ def update(
external_id: A user provided id that can be used to link an account holder with an external
system
+ naics_code: 6-digit North American Industry Classification System (NAICS) code for the
+ business.
+
nature_of_business: Short description of the company's line of business (i.e., what does the company
do?).
@@ -639,6 +653,7 @@ def update(
business_entity: account_holder_update_params.KYBPatchRequestBusinessEntity | Omit = omit,
control_person: account_holder_update_params.KYBPatchRequestControlPerson | Omit = omit,
external_id: str | Omit = omit,
+ naics_code: str | Omit = omit,
nature_of_business: str | Omit = omit,
website_url: str | Omit = omit,
individual: account_holder_update_params.KYCPatchRequestIndividual | Omit = omit,
@@ -671,6 +686,7 @@ def update(
"business_entity": business_entity,
"control_person": control_person,
"external_id": external_id,
+ "naics_code": naics_code,
"nature_of_business": nature_of_business,
"website_url": website_url,
"individual": individual,
@@ -954,7 +970,7 @@ def simulate_enrollment_review(
self,
*,
account_holder_token: str | Omit = omit,
- status: Literal["ACCEPTED", "REJECTED"] | Omit = omit,
+ status: Literal["ACCEPTED", "REJECTED", "PENDING_REVIEW"] | Omit = omit,
status_reasons: List[
Literal[
"PRIMARY_BUSINESS_ENTITY_ID_VERIFICATION_FAILURE",
@@ -1140,6 +1156,7 @@ async def create(
beneficial_owner_entities: Iterable[account_holder_create_params.KYBBeneficialOwnerEntity] | Omit = omit,
external_id: str | Omit = omit,
kyb_passed_timestamp: str | Omit = omit,
+ naics_code: str | Omit = omit,
website_url: 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.
@@ -1197,6 +1214,9 @@ async def create(
This field is required only if workflow type is `KYB_BYO`.
+ naics_code: 6-digit North American Industry Classification System (NAICS) code for the
+ business.
+
website_url: Company website URL.
extra_headers: Send extra headers
@@ -1218,6 +1238,7 @@ async def create(
| Omit = omit,
control_person: account_holder_create_params.KYBDelegatedControlPerson | Omit = omit,
external_id: str | Omit = omit,
+ naics_code: str | Omit = omit,
nature_of_business: str | Omit = omit,
tos_timestamp: str | Omit = omit,
website_url: str | Omit = omit,
@@ -1261,6 +1282,9 @@ async def create(
external_id: A user provided id that can be used to link an account holder with an external
system
+ naics_code: 6-digit North American Industry Classification System (NAICS) code for the
+ business.
+
nature_of_business: Short description of the company's line of business (i.e., what does the company
do?).
@@ -1434,6 +1458,7 @@ async def create(
beneficial_owner_entities: Iterable[account_holder_create_params.KYBBeneficialOwnerEntity] | Omit = omit,
external_id: str | Omit = omit,
kyb_passed_timestamp: str | Omit = omit,
+ naics_code: str | Omit = omit,
website_url: str | Omit = omit,
individual: account_holder_create_params.KYCIndividual | Omit = omit,
kyc_passed_timestamp: str | Omit = omit,
@@ -1466,6 +1491,7 @@ async def create(
"beneficial_owner_entities": beneficial_owner_entities,
"external_id": external_id,
"kyb_passed_timestamp": kyb_passed_timestamp,
+ "naics_code": naics_code,
"website_url": website_url,
"individual": individual,
"kyc_passed_timestamp": kyc_passed_timestamp,
@@ -1533,6 +1559,7 @@ async def update(
business_entity: account_holder_update_params.KYBPatchRequestBusinessEntity | Omit = omit,
control_person: account_holder_update_params.KYBPatchRequestControlPerson | Omit = omit,
external_id: str | Omit = omit,
+ naics_code: str | Omit = omit,
nature_of_business: str | Omit = omit,
website_url: str | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -1579,6 +1606,9 @@ async def update(
external_id: A user provided id that can be used to link an account holder with an external
system
+ naics_code: 6-digit North American Industry Classification System (NAICS) code for the
+ business.
+
nature_of_business: Short description of the company's line of business (i.e., what does the company
do?).
@@ -1709,6 +1739,7 @@ async def update(
business_entity: account_holder_update_params.KYBPatchRequestBusinessEntity | Omit = omit,
control_person: account_holder_update_params.KYBPatchRequestControlPerson | Omit = omit,
external_id: str | Omit = omit,
+ naics_code: str | Omit = omit,
nature_of_business: str | Omit = omit,
website_url: str | Omit = omit,
individual: account_holder_update_params.KYCPatchRequestIndividual | Omit = omit,
@@ -1741,6 +1772,7 @@ async def update(
"business_entity": business_entity,
"control_person": control_person,
"external_id": external_id,
+ "naics_code": naics_code,
"nature_of_business": nature_of_business,
"website_url": website_url,
"individual": individual,
@@ -2024,7 +2056,7 @@ async def simulate_enrollment_review(
self,
*,
account_holder_token: str | Omit = omit,
- status: Literal["ACCEPTED", "REJECTED"] | Omit = omit,
+ status: Literal["ACCEPTED", "REJECTED", "PENDING_REVIEW"] | Omit = omit,
status_reasons: List[
Literal[
"PRIMARY_BUSINESS_ENTITY_ID_VERIFICATION_FAILURE",
diff --git a/src/lithic/resources/auth_rules/v2/v2.py b/src/lithic/resources/auth_rules/v2/v2.py
index 6e078eb2..6a212e97 100644
--- a/src/lithic/resources/auth_rules/v2/v2.py
+++ b/src/lithic/resources/auth_rules/v2/v2.py
@@ -2,7 +2,7 @@
from __future__ import annotations
-from typing import List, Union, Optional
+from typing import Any, List, Union, Optional, cast
from datetime import date
from typing_extensions import Literal, overload
@@ -30,11 +30,13 @@
v2_draft_params,
v2_create_params,
v2_update_params,
+ v2_list_results_params,
v2_retrieve_report_params,
v2_retrieve_features_params,
)
from ....types.auth_rules.auth_rule import AuthRule
from ....types.auth_rules.event_stream import EventStream
+from ....types.auth_rules.v2_list_results_response import V2ListResultsResponse
from ....types.auth_rules.v2_retrieve_report_response import V2RetrieveReportResponse
from ....types.auth_rules.v2_retrieve_features_response import V2RetrieveFeaturesResponse
@@ -625,6 +627,78 @@ def draft(
cast_to=AuthRule,
)
+ def list_results(
+ self,
+ *,
+ auth_rule_token: str | Omit = omit,
+ ending_before: str | Omit = omit,
+ event_token: str | Omit = omit,
+ has_actions: bool | Omit = omit,
+ page_size: int | Omit = omit,
+ starting_after: 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,
+ ) -> SyncCursorPage[V2ListResultsResponse]:
+ """
+ Lists Auth Rule evaluation results.
+
+ **Limitations:**
+
+ - Results are available for the past 3 months only
+ - At least one filter (`event_token` or `auth_rule_token`) must be provided
+ - When filtering by `event_token`, pagination is not supported
+
+ Args:
+ auth_rule_token: Filter by Auth Rule token
+
+ ending_before: A cursor representing an item's token before which a page of results should end.
+ Used to retrieve the previous page of results before this item.
+
+ event_token: Filter by event token
+
+ has_actions: Filter by whether the rule evaluation produced any actions. When not provided,
+ all results are returned.
+
+ page_size: Page size (for pagination).
+
+ starting_after: A cursor representing an item's token after which a page of results should
+ begin. Used to retrieve the next page of results after this item.
+
+ 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_api_list(
+ "/v2/auth_rules/results",
+ page=SyncCursorPage[V2ListResultsResponse],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "auth_rule_token": auth_rule_token,
+ "ending_before": ending_before,
+ "event_token": event_token,
+ "has_actions": has_actions,
+ "page_size": page_size,
+ "starting_after": starting_after,
+ },
+ v2_list_results_params.V2ListResultsParams,
+ ),
+ ),
+ model=cast(Any, V2ListResultsResponse), # Union types cannot be passed in as arguments in the type system
+ )
+
def promote(
self,
auth_rule_token: str,
@@ -1358,6 +1432,78 @@ async def draft(
cast_to=AuthRule,
)
+ def list_results(
+ self,
+ *,
+ auth_rule_token: str | Omit = omit,
+ ending_before: str | Omit = omit,
+ event_token: str | Omit = omit,
+ has_actions: bool | Omit = omit,
+ page_size: int | Omit = omit,
+ starting_after: 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,
+ ) -> AsyncPaginator[V2ListResultsResponse, AsyncCursorPage[V2ListResultsResponse]]:
+ """
+ Lists Auth Rule evaluation results.
+
+ **Limitations:**
+
+ - Results are available for the past 3 months only
+ - At least one filter (`event_token` or `auth_rule_token`) must be provided
+ - When filtering by `event_token`, pagination is not supported
+
+ Args:
+ auth_rule_token: Filter by Auth Rule token
+
+ ending_before: A cursor representing an item's token before which a page of results should end.
+ Used to retrieve the previous page of results before this item.
+
+ event_token: Filter by event token
+
+ has_actions: Filter by whether the rule evaluation produced any actions. When not provided,
+ all results are returned.
+
+ page_size: Page size (for pagination).
+
+ starting_after: A cursor representing an item's token after which a page of results should
+ begin. Used to retrieve the next page of results after this item.
+
+ 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_api_list(
+ "/v2/auth_rules/results",
+ page=AsyncCursorPage[V2ListResultsResponse],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "auth_rule_token": auth_rule_token,
+ "ending_before": ending_before,
+ "event_token": event_token,
+ "has_actions": has_actions,
+ "page_size": page_size,
+ "starting_after": starting_after,
+ },
+ v2_list_results_params.V2ListResultsParams,
+ ),
+ ),
+ model=cast(Any, V2ListResultsResponse), # Union types cannot be passed in as arguments in the type system
+ )
+
async def promote(
self,
auth_rule_token: str,
@@ -1529,6 +1675,9 @@ def __init__(self, v2: V2) -> None:
self.draft = _legacy_response.to_raw_response_wrapper(
v2.draft,
)
+ self.list_results = _legacy_response.to_raw_response_wrapper(
+ v2.list_results,
+ )
self.promote = _legacy_response.to_raw_response_wrapper(
v2.promote,
)
@@ -1566,6 +1715,9 @@ def __init__(self, v2: AsyncV2) -> None:
self.draft = _legacy_response.async_to_raw_response_wrapper(
v2.draft,
)
+ self.list_results = _legacy_response.async_to_raw_response_wrapper(
+ v2.list_results,
+ )
self.promote = _legacy_response.async_to_raw_response_wrapper(
v2.promote,
)
@@ -1603,6 +1755,9 @@ def __init__(self, v2: V2) -> None:
self.draft = to_streamed_response_wrapper(
v2.draft,
)
+ self.list_results = to_streamed_response_wrapper(
+ v2.list_results,
+ )
self.promote = to_streamed_response_wrapper(
v2.promote,
)
@@ -1640,6 +1795,9 @@ def __init__(self, v2: AsyncV2) -> None:
self.draft = async_to_streamed_response_wrapper(
v2.draft,
)
+ self.list_results = async_to_streamed_response_wrapper(
+ v2.list_results,
+ )
self.promote = async_to_streamed_response_wrapper(
v2.promote,
)
diff --git a/src/lithic/resources/book_transfers.py b/src/lithic/resources/book_transfers.py
index 1fbef4bf..eeafcc0a 100644
--- a/src/lithic/resources/book_transfers.py
+++ b/src/lithic/resources/book_transfers.py
@@ -104,6 +104,7 @@ def create(
],
token: str | Omit = omit,
external_id: str | Omit = omit,
+ hold_token: str | Omit = omit,
memo: str | Omit = omit,
on_closed_account: Literal["FAIL", "USE_SUSPENSE"] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -136,6 +137,8 @@ def create(
external_id: External ID defined by the customer
+ hold_token: Token of an existing hold to settle when this transfer is initiated
+
memo: Optional descriptor for the transfer.
on_closed_account: What to do if the financial account is closed when posting an operation
@@ -160,6 +163,7 @@ def create(
"type": type,
"token": token,
"external_id": external_id,
+ "hold_token": hold_token,
"memo": memo,
"on_closed_account": on_closed_account,
},
@@ -459,6 +463,7 @@ async def create(
],
token: str | Omit = omit,
external_id: str | Omit = omit,
+ hold_token: str | Omit = omit,
memo: str | Omit = omit,
on_closed_account: Literal["FAIL", "USE_SUSPENSE"] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -491,6 +496,8 @@ async def create(
external_id: External ID defined by the customer
+ hold_token: Token of an existing hold to settle when this transfer is initiated
+
memo: Optional descriptor for the transfer.
on_closed_account: What to do if the financial account is closed when posting an operation
@@ -515,6 +522,7 @@ async def create(
"type": type,
"token": token,
"external_id": external_id,
+ "hold_token": hold_token,
"memo": memo,
"on_closed_account": on_closed_account,
},
diff --git a/src/lithic/types/account_holder.py b/src/lithic/types/account_holder.py
index dbe28588..bf384966 100644
--- a/src/lithic/types/account_holder.py
+++ b/src/lithic/types/account_holder.py
@@ -290,6 +290,13 @@ class AccountHolder(BaseModel):
is being run.
"""
+ naics_code: Optional[str] = None
+ """Only present when user_type == "BUSINESS".
+
+ 6-digit North American Industry Classification System (NAICS) code for the
+ business.
+ """
+
nature_of_business: Optional[str] = None
"""Only present when user_type == "BUSINESS".
@@ -347,8 +354,8 @@ class AccountHolder(BaseModel):
If the type is "INDIVIDUAL", the "individual" attribute will be present. If the
type is "BUSINESS" then the "business_entity", "control_person",
- "beneficial_owner_individuals", "nature_of_business", and "website_url"
- attributes will be present.
+ "beneficial_owner_individuals", "naics_code", "nature_of_business", and
+ "website_url" attributes will be present.
"""
verification_application: Optional[VerificationApplication] = None
diff --git a/src/lithic/types/account_holder_create_params.py b/src/lithic/types/account_holder_create_params.py
index c90cfad2..2c0a27cb 100644
--- a/src/lithic/types/account_holder_create_params.py
+++ b/src/lithic/types/account_holder_create_params.py
@@ -87,6 +87,12 @@ class KYB(TypedDict, total=False):
This field is required only if workflow type is `KYB_BYO`.
"""
+ naics_code: str
+ """
+ 6-digit North American Industry Classification System (NAICS) code for the
+ business.
+ """
+
website_url: str
"""Company website URL."""
@@ -270,6 +276,12 @@ class KYBDelegated(TypedDict, total=False):
system
"""
+ naics_code: str
+ """
+ 6-digit North American Industry Classification System (NAICS) code for the
+ business.
+ """
+
nature_of_business: str
"""
Short description of the company's line of business (i.e., what does the company
diff --git a/src/lithic/types/account_holder_simulate_enrollment_review_params.py b/src/lithic/types/account_holder_simulate_enrollment_review_params.py
index d098b2e0..ac9331d9 100644
--- a/src/lithic/types/account_holder_simulate_enrollment_review_params.py
+++ b/src/lithic/types/account_holder_simulate_enrollment_review_params.py
@@ -12,7 +12,7 @@ class AccountHolderSimulateEnrollmentReviewParams(TypedDict, total=False):
account_holder_token: str
"""The account holder which to perform the simulation upon."""
- status: Literal["ACCEPTED", "REJECTED"]
+ status: Literal["ACCEPTED", "REJECTED", "PENDING_REVIEW"]
"""An account holder's status for use within the simulation."""
status_reasons: List[
diff --git a/src/lithic/types/account_holder_simulate_enrollment_review_response.py b/src/lithic/types/account_holder_simulate_enrollment_review_response.py
index 56f53d0d..b2785883 100644
--- a/src/lithic/types/account_holder_simulate_enrollment_review_response.py
+++ b/src/lithic/types/account_holder_simulate_enrollment_review_response.py
@@ -355,6 +355,13 @@ class AccountHolderSimulateEnrollmentReviewResponse(BaseModel):
is being run.
"""
+ naics_code: Optional[str] = None
+ """Only present when user_type == "BUSINESS".
+
+ 6-digit North American Industry Classification System (NAICS) code for the
+ business.
+ """
+
nature_of_business: Optional[str] = None
"""Only present when user_type == "BUSINESS".
@@ -425,8 +432,8 @@ class AccountHolderSimulateEnrollmentReviewResponse(BaseModel):
If the type is "INDIVIDUAL", the "individual" attribute will be present.
If the type is "BUSINESS" then the "business_entity", "control_person",
- "beneficial_owner_individuals", "nature_of_business", and "website_url"
- attributes will be present.
+ "beneficial_owner_individuals", "naics_code", "nature_of_business", and
+ "website_url" attributes will be present.
"""
verification_application: Optional[VerificationApplication] = None
diff --git a/src/lithic/types/account_holder_update_params.py b/src/lithic/types/account_holder_update_params.py
index ed23ca4a..2551e6c4 100644
--- a/src/lithic/types/account_holder_update_params.py
+++ b/src/lithic/types/account_holder_update_params.py
@@ -59,6 +59,12 @@ class KYBPatchRequest(TypedDict, total=False):
system
"""
+ naics_code: str
+ """
+ 6-digit North American Industry Classification System (NAICS) code for the
+ business.
+ """
+
nature_of_business: str
"""
Short description of the company's line of business (i.e., what does the company
diff --git a/src/lithic/types/account_holder_update_response.py b/src/lithic/types/account_holder_update_response.py
index 7c8a1340..304b81de 100644
--- a/src/lithic/types/account_holder_update_response.py
+++ b/src/lithic/types/account_holder_update_response.py
@@ -358,6 +358,13 @@ class KYBKYCPatchResponse(BaseModel):
is being run.
"""
+ naics_code: Optional[str] = None
+ """Only present when user_type == "BUSINESS".
+
+ 6-digit North American Industry Classification System (NAICS) code for the
+ business.
+ """
+
nature_of_business: Optional[str] = None
"""Only present when user_type == "BUSINESS".
@@ -428,8 +435,8 @@ class KYBKYCPatchResponse(BaseModel):
If the type is "INDIVIDUAL", the "individual" attribute will be present.
If the type is "BUSINESS" then the "business_entity", "control_person",
- "beneficial_owner_individuals", "nature_of_business", and "website_url"
- attributes will be present.
+ "beneficial_owner_individuals", "naics_code", "nature_of_business", and
+ "website_url" attributes will be present.
"""
verification_application: Optional[KYBKYCPatchResponseVerificationApplication] = None
diff --git a/src/lithic/types/account_holder_updated_webhook_event.py b/src/lithic/types/account_holder_updated_webhook_event.py
index fcda2c62..34bfa0fc 100644
--- a/src/lithic/types/account_holder_updated_webhook_event.py
+++ b/src/lithic/types/account_holder_updated_webhook_event.py
@@ -206,6 +206,12 @@ class KYBPayload(BaseModel):
system
"""
+ naics_code: Optional[str] = None
+ """
+ 6-digit North American Industry Classification System (NAICS) code for the
+ business. Only present if naics_code was included in the update request.
+ """
+
nature_of_business: Optional[str] = None
"""
Short description of the company's line of business (i.e., what does the company
diff --git a/src/lithic/types/auth_rules/__init__.py b/src/lithic/types/auth_rules/__init__.py
index 7f4cb281..b064d781 100644
--- a/src/lithic/types/auth_rules/__init__.py
+++ b/src/lithic/types/auth_rules/__init__.py
@@ -15,8 +15,10 @@
from .conditional_operation import ConditionalOperation as ConditionalOperation
from .velocity_limit_params import VelocityLimitParams as VelocityLimitParams
from .velocity_limit_period import VelocityLimitPeriod as VelocityLimitPeriod
+from .v2_list_results_params import V2ListResultsParams as V2ListResultsParams
from .conditional_value_param import ConditionalValueParam as ConditionalValueParam
from .merchant_lock_parameters import MerchantLockParameters as MerchantLockParameters
+from .v2_list_results_response import V2ListResultsResponse as V2ListResultsResponse
from .auth_rule_condition_param import AuthRuleConditionParam as AuthRuleConditionParam
from .v2_retrieve_report_params import V2RetrieveReportParams as V2RetrieveReportParams
from .v2_retrieve_features_params import V2RetrieveFeaturesParams as V2RetrieveFeaturesParams
diff --git a/src/lithic/types/auth_rules/conditional_ach_action_parameters.py b/src/lithic/types/auth_rules/conditional_ach_action_parameters.py
index 076a4dfc..38601fb7 100644
--- a/src/lithic/types/auth_rules/conditional_ach_action_parameters.py
+++ b/src/lithic/types/auth_rules/conditional_ach_action_parameters.py
@@ -129,6 +129,6 @@ class Condition(BaseModel):
class ConditionalACHActionParameters(BaseModel):
action: Action
- """The action to take if the conditions are met"""
+ """The action to take if the conditions are met."""
conditions: List[Condition]
diff --git a/src/lithic/types/auth_rules/conditional_ach_action_parameters_param.py b/src/lithic/types/auth_rules/conditional_ach_action_parameters_param.py
index 5c29f6e6..a11fdafd 100644
--- a/src/lithic/types/auth_rules/conditional_ach_action_parameters_param.py
+++ b/src/lithic/types/auth_rules/conditional_ach_action_parameters_param.py
@@ -133,6 +133,6 @@ class Condition(TypedDict, total=False):
class ConditionalACHActionParametersParam(TypedDict, total=False):
action: Required[Action]
- """The action to take if the conditions are met"""
+ """The action to take if the conditions are met."""
conditions: Required[Iterable[Condition]]
diff --git a/src/lithic/types/auth_rules/conditional_tokenization_action_parameters.py b/src/lithic/types/auth_rules/conditional_tokenization_action_parameters.py
index 127da6df..a6b390d8 100644
--- a/src/lithic/types/auth_rules/conditional_tokenization_action_parameters.py
+++ b/src/lithic/types/auth_rules/conditional_tokenization_action_parameters.py
@@ -89,8 +89,10 @@ class Condition(BaseModel):
- `TIMESTAMP`: The timestamp of the tokenization request in ISO 8601 format.
- `TOKENIZATION_CHANNEL`: The channel through which the tokenization request was
- initiated (e.g., DIGITAL_WALLET, ECOMMERCE).
- - `TOKENIZATION_SOURCE`: The source of the tokenization request.
+ initiated. Valid values are `DIGITAL_WALLET`, `MERCHANT`.
+ - `TOKENIZATION_SOURCE`: The source of the tokenization request. Valid values
+ are `ACCOUNT_ON_FILE`, `MANUAL_PROVISION`, `PUSH_PROVISION`, `CHIP_DIP`,
+ `CONTACTLESS_TAP`, `TOKEN`, `UNKNOWN`.
- `TOKEN_REQUESTOR_NAME`: The name of the entity requesting the token. Valid
values are `ALT_ID`, `AMAZON_ONE`, `AMERICAN_EXPRESS_TOKEN_SERVICE`,
`ANDROID_PAY`, `APPLE_PAY`, `FACEBOOK`, `FITBIT_PAY`, `GARMIN_PAY`,
@@ -128,6 +130,6 @@ class Condition(BaseModel):
class ConditionalTokenizationActionParameters(BaseModel):
action: Action
- """The action to take if the conditions are met"""
+ """The action to take if the conditions are met."""
conditions: List[Condition]
diff --git a/src/lithic/types/auth_rules/conditional_tokenization_action_parameters_param.py b/src/lithic/types/auth_rules/conditional_tokenization_action_parameters_param.py
index e25a0d58..388cd491 100644
--- a/src/lithic/types/auth_rules/conditional_tokenization_action_parameters_param.py
+++ b/src/lithic/types/auth_rules/conditional_tokenization_action_parameters_param.py
@@ -89,8 +89,10 @@ class Condition(TypedDict, total=False):
- `TIMESTAMP`: The timestamp of the tokenization request in ISO 8601 format.
- `TOKENIZATION_CHANNEL`: The channel through which the tokenization request was
- initiated (e.g., DIGITAL_WALLET, ECOMMERCE).
- - `TOKENIZATION_SOURCE`: The source of the tokenization request.
+ initiated. Valid values are `DIGITAL_WALLET`, `MERCHANT`.
+ - `TOKENIZATION_SOURCE`: The source of the tokenization request. Valid values
+ are `ACCOUNT_ON_FILE`, `MANUAL_PROVISION`, `PUSH_PROVISION`, `CHIP_DIP`,
+ `CONTACTLESS_TAP`, `TOKEN`, `UNKNOWN`.
- `TOKEN_REQUESTOR_NAME`: The name of the entity requesting the token. Valid
values are `ALT_ID`, `AMAZON_ONE`, `AMERICAN_EXPRESS_TOKEN_SERVICE`,
`ANDROID_PAY`, `APPLE_PAY`, `FACEBOOK`, `FITBIT_PAY`, `GARMIN_PAY`,
@@ -128,6 +130,6 @@ class Condition(TypedDict, total=False):
class ConditionalTokenizationActionParametersParam(TypedDict, total=False):
action: Required[Action]
- """The action to take if the conditions are met"""
+ """The action to take if the conditions are met."""
conditions: Required[Iterable[Condition]]
diff --git a/src/lithic/types/auth_rules/v2_list_results_params.py b/src/lithic/types/auth_rules/v2_list_results_params.py
new file mode 100644
index 00000000..32ba0cd2
--- /dev/null
+++ b/src/lithic/types/auth_rules/v2_list_results_params.py
@@ -0,0 +1,37 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["V2ListResultsParams"]
+
+
+class V2ListResultsParams(TypedDict, total=False):
+ auth_rule_token: str
+ """Filter by Auth Rule token"""
+
+ ending_before: str
+ """A cursor representing an item's token before which a page of results should end.
+
+ Used to retrieve the previous page of results before this item.
+ """
+
+ event_token: str
+ """Filter by event token"""
+
+ has_actions: bool
+ """Filter by whether the rule evaluation produced any actions.
+
+ When not provided, all results are returned.
+ """
+
+ page_size: int
+ """Page size (for pagination)."""
+
+ starting_after: str
+ """A cursor representing an item's token after which a page of results should
+ begin.
+
+ Used to retrieve the next page of results after this item.
+ """
diff --git a/src/lithic/types/auth_rules/v2_list_results_response.py b/src/lithic/types/auth_rules/v2_list_results_response.py
new file mode 100644
index 00000000..4d8ecd76
--- /dev/null
+++ b/src/lithic/types/auth_rules/v2_list_results_response.py
@@ -0,0 +1,302 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Union, Optional
+from datetime import datetime
+from typing_extensions import Literal, TypeAlias
+
+from ..._models import BaseModel
+
+__all__ = [
+ "V2ListResultsResponse",
+ "AuthorizationResult",
+ "AuthorizationResultAction",
+ "Authentication3DSResult",
+ "Authentication3DsResultAction",
+ "TokenizationResult",
+ "TokenizationResultAction",
+ "TokenizationResultActionDeclineAction",
+ "TokenizationResultActionRequireTfaAction",
+ "ACHResult",
+ "ACHResultAction",
+ "ACHResultActionApproveAction",
+ "ACHResultActionReturnAction",
+]
+
+
+class AuthorizationResultAction(BaseModel):
+ type: Literal["DECLINE", "CHALLENGE"]
+
+ explanation: Optional[str] = None
+ """Optional explanation for why this action was taken"""
+
+
+class AuthorizationResult(BaseModel):
+ token: str
+ """Globally unique identifier for the evaluation"""
+
+ actions: List[AuthorizationResultAction]
+ """Actions returned by the rule evaluation"""
+
+ auth_rule_token: str
+ """The Auth Rule token"""
+
+ evaluation_time: datetime
+ """Timestamp of the rule evaluation"""
+
+ event_stream: Literal["AUTHORIZATION"]
+ """The event stream during which the rule was evaluated"""
+
+ event_token: str
+ """Token of the event that triggered the evaluation"""
+
+ mode: Literal["ACTIVE", "INACTIVE"]
+ """The state of the Auth Rule"""
+
+ rule_version: int
+ """Version of the rule that was evaluated"""
+
+
+class Authentication3DsResultAction(BaseModel):
+ type: Literal["DECLINE", "CHALLENGE"]
+
+ explanation: Optional[str] = None
+ """Optional explanation for why this action was taken"""
+
+
+class Authentication3DSResult(BaseModel):
+ token: str
+ """Globally unique identifier for the evaluation"""
+
+ actions: List[Authentication3DsResultAction]
+ """Actions returned by the rule evaluation"""
+
+ auth_rule_token: str
+ """The Auth Rule token"""
+
+ evaluation_time: datetime
+ """Timestamp of the rule evaluation"""
+
+ event_stream: Literal["THREE_DS_AUTHENTICATION"]
+ """The event stream during which the rule was evaluated"""
+
+ event_token: str
+ """Token of the event that triggered the evaluation"""
+
+ mode: Literal["ACTIVE", "INACTIVE"]
+ """The state of the Auth Rule"""
+
+ rule_version: int
+ """Version of the rule that was evaluated"""
+
+
+class TokenizationResultActionDeclineAction(BaseModel):
+ type: Literal["DECLINE"]
+ """Decline the tokenization request"""
+
+ explanation: Optional[str] = None
+ """Optional explanation for why this action was taken"""
+
+ reason: Optional[
+ Literal[
+ "ACCOUNT_SCORE_1",
+ "DEVICE_SCORE_1",
+ "ALL_WALLET_DECLINE_REASONS_PRESENT",
+ "WALLET_RECOMMENDED_DECISION_RED",
+ "CVC_MISMATCH",
+ "CARD_EXPIRY_MONTH_MISMATCH",
+ "CARD_EXPIRY_YEAR_MISMATCH",
+ "CARD_INVALID_STATE",
+ "CUSTOMER_RED_PATH",
+ "INVALID_CUSTOMER_RESPONSE",
+ "NETWORK_FAILURE",
+ "GENERIC_DECLINE",
+ "DIGITAL_CARD_ART_REQUIRED",
+ ]
+ ] = None
+ """Reason code for declining the tokenization request"""
+
+
+class TokenizationResultActionRequireTfaAction(BaseModel):
+ type: Literal["REQUIRE_TFA"]
+ """Require two-factor authentication for the tokenization request"""
+
+ explanation: Optional[str] = None
+ """Optional explanation for why this action was taken"""
+
+ reason: Optional[
+ Literal[
+ "WALLET_RECOMMENDED_TFA",
+ "SUSPICIOUS_ACTIVITY",
+ "DEVICE_RECENTLY_LOST",
+ "TOO_MANY_RECENT_ATTEMPTS",
+ "TOO_MANY_RECENT_TOKENS",
+ "TOO_MANY_DIFFERENT_CARDHOLDERS",
+ "OUTSIDE_HOME_TERRITORY",
+ "HAS_SUSPENDED_TOKENS",
+ "HIGH_RISK",
+ "ACCOUNT_SCORE_LOW",
+ "DEVICE_SCORE_LOW",
+ "CARD_STATE_TFA",
+ "HARDCODED_TFA",
+ "CUSTOMER_RULE_TFA",
+ "DEVICE_HOST_CARD_EMULATION",
+ ]
+ ] = None
+ """Reason code for requiring two-factor authentication"""
+
+
+TokenizationResultAction: TypeAlias = Union[
+ TokenizationResultActionDeclineAction, TokenizationResultActionRequireTfaAction
+]
+
+
+class TokenizationResult(BaseModel):
+ token: str
+ """Globally unique identifier for the evaluation"""
+
+ actions: List[TokenizationResultAction]
+ """Actions returned by the rule evaluation"""
+
+ auth_rule_token: str
+ """The Auth Rule token"""
+
+ evaluation_time: datetime
+ """Timestamp of the rule evaluation"""
+
+ event_stream: Literal["TOKENIZATION"]
+ """The event stream during which the rule was evaluated"""
+
+ event_token: str
+ """Token of the event that triggered the evaluation"""
+
+ mode: Literal["ACTIVE", "INACTIVE"]
+ """The state of the Auth Rule"""
+
+ rule_version: int
+ """Version of the rule that was evaluated"""
+
+
+class ACHResultActionApproveAction(BaseModel):
+ type: Literal["APPROVE"]
+ """Approve the ACH transaction"""
+
+ explanation: Optional[str] = None
+ """Optional explanation for why this action was taken"""
+
+
+class ACHResultActionReturnAction(BaseModel):
+ code: Literal[
+ "R01",
+ "R02",
+ "R03",
+ "R04",
+ "R05",
+ "R06",
+ "R07",
+ "R08",
+ "R09",
+ "R10",
+ "R11",
+ "R12",
+ "R13",
+ "R14",
+ "R15",
+ "R16",
+ "R17",
+ "R18",
+ "R19",
+ "R20",
+ "R21",
+ "R22",
+ "R23",
+ "R24",
+ "R25",
+ "R26",
+ "R27",
+ "R28",
+ "R29",
+ "R30",
+ "R31",
+ "R32",
+ "R33",
+ "R34",
+ "R35",
+ "R36",
+ "R37",
+ "R38",
+ "R39",
+ "R40",
+ "R41",
+ "R42",
+ "R43",
+ "R44",
+ "R45",
+ "R46",
+ "R47",
+ "R50",
+ "R51",
+ "R52",
+ "R53",
+ "R61",
+ "R62",
+ "R67",
+ "R68",
+ "R69",
+ "R70",
+ "R71",
+ "R72",
+ "R73",
+ "R74",
+ "R75",
+ "R76",
+ "R77",
+ "R80",
+ "R81",
+ "R82",
+ "R83",
+ "R84",
+ "R85",
+ ]
+ """NACHA return code to use when returning the transaction.
+
+ Note that the list of available return codes is subject to an allowlist
+ configured at the program level
+ """
+
+ type: Literal["RETURN"]
+ """Return the ACH transaction"""
+
+ explanation: Optional[str] = None
+ """Optional explanation for why this action was taken"""
+
+
+ACHResultAction: TypeAlias = Union[ACHResultActionApproveAction, ACHResultActionReturnAction]
+
+
+class ACHResult(BaseModel):
+ token: str
+ """Globally unique identifier for the evaluation"""
+
+ actions: List[ACHResultAction]
+ """Actions returned by the rule evaluation"""
+
+ auth_rule_token: str
+ """The Auth Rule token"""
+
+ evaluation_time: datetime
+ """Timestamp of the rule evaluation"""
+
+ event_stream: Literal["ACH_CREDIT_RECEIPT", "ACH_DEBIT_RECEIPT"]
+ """The event stream during which the rule was evaluated"""
+
+ event_token: str
+ """Token of the event that triggered the evaluation"""
+
+ mode: Literal["ACTIVE", "INACTIVE"]
+ """The state of the Auth Rule"""
+
+ rule_version: int
+ """Version of the rule that was evaluated"""
+
+
+V2ListResultsResponse: TypeAlias = Union[AuthorizationResult, Authentication3DSResult, TokenizationResult, ACHResult]
diff --git a/src/lithic/types/book_transfer_create_params.py b/src/lithic/types/book_transfer_create_params.py
index 584734cf..b5cab3b2 100644
--- a/src/lithic/types/book_transfer_create_params.py
+++ b/src/lithic/types/book_transfer_create_params.py
@@ -93,6 +93,9 @@ class BookTransferCreateParams(TypedDict, total=False):
external_id: str
"""External ID defined by the customer"""
+ hold_token: str
+ """Token of an existing hold to settle when this transfer is initiated"""
+
memo: str
"""Optional descriptor for the transfer."""
diff --git a/src/lithic/types/kyb_param.py b/src/lithic/types/kyb_param.py
index b3131cd4..b02c8749 100644
--- a/src/lithic/types/kyb_param.py
+++ b/src/lithic/types/kyb_param.py
@@ -220,5 +220,11 @@ class KYBParam(TypedDict, total=False):
This field is required only if workflow type is `KYB_BYO`.
"""
+ naics_code: str
+ """
+ 6-digit North American Industry Classification System (NAICS) code for the
+ business.
+ """
+
website_url: str
"""Company website URL."""
diff --git a/src/lithic/types/parsed_webhook_event.py b/src/lithic/types/parsed_webhook_event.py
index add8c01e..a9db122e 100644
--- a/src/lithic/types/parsed_webhook_event.py
+++ b/src/lithic/types/parsed_webhook_event.py
@@ -271,6 +271,12 @@ class KYBPayload(BaseModel):
system
"""
+ naics_code: Optional[str] = None
+ """
+ 6-digit North American Industry Classification System (NAICS) code for the
+ business. Only present if naics_code was included in the update request.
+ """
+
nature_of_business: Optional[str] = None
"""
Short description of the company's line of business (i.e., what does the company
diff --git a/tests/api_resources/auth_rules/test_v2.py b/tests/api_resources/auth_rules/test_v2.py
index 3167da4d..b7f3939c 100644
--- a/tests/api_resources/auth_rules/test_v2.py
+++ b/tests/api_resources/auth_rules/test_v2.py
@@ -13,6 +13,7 @@
from lithic.pagination import SyncCursorPage, AsyncCursorPage
from lithic.types.auth_rules import (
AuthRule,
+ V2ListResultsResponse,
V2RetrieveReportResponse,
V2RetrieveFeaturesResponse,
)
@@ -578,6 +579,43 @@ def test_path_params_draft(self, client: Lithic) -> None:
auth_rule_token="",
)
+ @parametrize
+ def test_method_list_results(self, client: Lithic) -> None:
+ v2 = client.auth_rules.v2.list_results()
+ assert_matches_type(SyncCursorPage[V2ListResultsResponse], v2, path=["response"])
+
+ @parametrize
+ def test_method_list_results_with_all_params(self, client: Lithic) -> None:
+ v2 = client.auth_rules.v2.list_results(
+ auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ending_before="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ event_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ has_actions=True,
+ page_size=1,
+ starting_after="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(SyncCursorPage[V2ListResultsResponse], v2, path=["response"])
+
+ @parametrize
+ def test_raw_response_list_results(self, client: Lithic) -> None:
+ response = client.auth_rules.v2.with_raw_response.list_results()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ v2 = response.parse()
+ assert_matches_type(SyncCursorPage[V2ListResultsResponse], v2, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list_results(self, client: Lithic) -> None:
+ with client.auth_rules.v2.with_streaming_response.list_results() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ v2 = response.parse()
+ assert_matches_type(SyncCursorPage[V2ListResultsResponse], v2, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
@parametrize
def test_method_promote(self, client: Lithic) -> None:
v2 = client.auth_rules.v2.promote(
@@ -1270,6 +1308,43 @@ async def test_path_params_draft(self, async_client: AsyncLithic) -> None:
auth_rule_token="",
)
+ @parametrize
+ async def test_method_list_results(self, async_client: AsyncLithic) -> None:
+ v2 = await async_client.auth_rules.v2.list_results()
+ assert_matches_type(AsyncCursorPage[V2ListResultsResponse], v2, path=["response"])
+
+ @parametrize
+ async def test_method_list_results_with_all_params(self, async_client: AsyncLithic) -> None:
+ v2 = await async_client.auth_rules.v2.list_results(
+ auth_rule_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ending_before="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ event_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ has_actions=True,
+ page_size=1,
+ starting_after="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(AsyncCursorPage[V2ListResultsResponse], v2, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list_results(self, async_client: AsyncLithic) -> None:
+ response = await async_client.auth_rules.v2.with_raw_response.list_results()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ v2 = response.parse()
+ assert_matches_type(AsyncCursorPage[V2ListResultsResponse], v2, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list_results(self, async_client: AsyncLithic) -> None:
+ async with async_client.auth_rules.v2.with_streaming_response.list_results() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ v2 = await response.parse()
+ assert_matches_type(AsyncCursorPage[V2ListResultsResponse], v2, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
@parametrize
async def test_method_promote(self, async_client: AsyncLithic) -> None:
v2 = await async_client.auth_rules.v2.promote(
diff --git a/tests/api_resources/test_account_holders.py b/tests/api_resources/test_account_holders.py
index 3c723a7d..a01b2b4d 100644
--- a/tests/api_resources/test_account_holders.py
+++ b/tests/api_resources/test_account_holders.py
@@ -151,6 +151,7 @@ def test_method_create_with_all_params_overload_1(self, client: Lithic) -> None:
],
external_id="external_id",
kyb_passed_timestamp="2018-05-29T21:16:05Z",
+ naics_code="541512",
website_url="www.mybusiness.com",
)
assert_matches_type(AccountHolderCreateResponse, account_holder, path=["response"])
@@ -336,6 +337,7 @@ def test_method_create_with_all_params_overload_2(self, client: Lithic) -> None:
"phone_number": "+15555555555",
},
external_id="external_id",
+ naics_code="541512",
nature_of_business="Software company selling solutions to the restaurant industry",
tos_timestamp="2022-03-08 08:00:00",
website_url="www.mybusiness.com",
@@ -699,6 +701,7 @@ def test_method_update_with_all_params_overload_1(self, client: Lithic) -> None:
"phone_number": "+15555555555",
},
external_id="external_id",
+ naics_code="541512",
nature_of_business="Software company selling solutions to the restaurant industry",
website_url="www.mybusiness.com",
)
@@ -1240,6 +1243,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn
],
external_id="external_id",
kyb_passed_timestamp="2018-05-29T21:16:05Z",
+ naics_code="541512",
website_url="www.mybusiness.com",
)
assert_matches_type(AccountHolderCreateResponse, account_holder, path=["response"])
@@ -1425,6 +1429,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn
"phone_number": "+15555555555",
},
external_id="external_id",
+ naics_code="541512",
nature_of_business="Software company selling solutions to the restaurant industry",
tos_timestamp="2022-03-08 08:00:00",
website_url="www.mybusiness.com",
@@ -1788,6 +1793,7 @@ async def test_method_update_with_all_params_overload_1(self, async_client: Asyn
"phone_number": "+15555555555",
},
external_id="external_id",
+ naics_code="541512",
nature_of_business="Software company selling solutions to the restaurant industry",
website_url="www.mybusiness.com",
)
diff --git a/tests/api_resources/test_book_transfers.py b/tests/api_resources/test_book_transfers.py
index 690dc830..8276909e 100644
--- a/tests/api_resources/test_book_transfers.py
+++ b/tests/api_resources/test_book_transfers.py
@@ -44,6 +44,7 @@ def test_method_create_with_all_params(self, client: Lithic) -> None:
type="ATM_BALANCE_INQUIRY",
token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
external_id="external_id",
+ hold_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
memo="memo",
on_closed_account="FAIL",
)
@@ -280,6 +281,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncLithic) ->
type="ATM_BALANCE_INQUIRY",
token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
external_id="external_id",
+ hold_token="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
memo="memo",
on_closed_account="FAIL",
)
diff --git a/tests/test_utils/test_json.py b/tests/test_utils/test_json.py
new file mode 100644
index 00000000..15e48fb1
--- /dev/null
+++ b/tests/test_utils/test_json.py
@@ -0,0 +1,126 @@
+from __future__ import annotations
+
+import datetime
+from typing import Union
+
+import pydantic
+
+from lithic import _compat
+from lithic._utils._json import openapi_dumps
+
+
+class TestOpenapiDumps:
+ def test_basic(self) -> None:
+ data = {"key": "value", "number": 42}
+ json_bytes = openapi_dumps(data)
+ assert json_bytes == b'{"key":"value","number":42}'
+
+ def test_datetime_serialization(self) -> None:
+ dt = datetime.datetime(2023, 1, 1, 12, 0, 0)
+ data = {"datetime": dt}
+ json_bytes = openapi_dumps(data)
+ assert json_bytes == b'{"datetime":"2023-01-01T12:00:00"}'
+
+ def test_pydantic_model_serialization(self) -> None:
+ class User(pydantic.BaseModel):
+ first_name: str
+ last_name: str
+ age: int
+
+ model_instance = User(first_name="John", last_name="Kramer", age=83)
+ data = {"model": model_instance}
+ json_bytes = openapi_dumps(data)
+ assert json_bytes == b'{"model":{"first_name":"John","last_name":"Kramer","age":83}}'
+
+ def test_pydantic_model_with_default_values(self) -> None:
+ class User(pydantic.BaseModel):
+ name: str
+ role: str = "user"
+ active: bool = True
+ score: int = 0
+
+ model_instance = User(name="Alice")
+ data = {"model": model_instance}
+ json_bytes = openapi_dumps(data)
+ assert json_bytes == b'{"model":{"name":"Alice"}}'
+
+ def test_pydantic_model_with_default_values_overridden(self) -> None:
+ class User(pydantic.BaseModel):
+ name: str
+ role: str = "user"
+ active: bool = True
+
+ model_instance = User(name="Bob", role="admin", active=False)
+ data = {"model": model_instance}
+ json_bytes = openapi_dumps(data)
+ assert json_bytes == b'{"model":{"name":"Bob","role":"admin","active":false}}'
+
+ def test_pydantic_model_with_alias(self) -> None:
+ class User(pydantic.BaseModel):
+ first_name: str = pydantic.Field(alias="firstName")
+ last_name: str = pydantic.Field(alias="lastName")
+
+ model_instance = User(firstName="John", lastName="Doe")
+ data = {"model": model_instance}
+ json_bytes = openapi_dumps(data)
+ assert json_bytes == b'{"model":{"firstName":"John","lastName":"Doe"}}'
+
+ def test_pydantic_model_with_alias_and_default(self) -> None:
+ class User(pydantic.BaseModel):
+ user_name: str = pydantic.Field(alias="userName")
+ user_role: str = pydantic.Field(default="member", alias="userRole")
+ is_active: bool = pydantic.Field(default=True, alias="isActive")
+
+ model_instance = User(userName="charlie")
+ data = {"model": model_instance}
+ json_bytes = openapi_dumps(data)
+ assert json_bytes == b'{"model":{"userName":"charlie"}}'
+
+ model_with_overrides = User(userName="diana", userRole="admin", isActive=False)
+ data = {"model": model_with_overrides}
+ json_bytes = openapi_dumps(data)
+ assert json_bytes == b'{"model":{"userName":"diana","userRole":"admin","isActive":false}}'
+
+ def test_pydantic_model_with_nested_models_and_defaults(self) -> None:
+ class Address(pydantic.BaseModel):
+ street: str
+ city: str = "Unknown"
+
+ class User(pydantic.BaseModel):
+ name: str
+ address: Address
+ verified: bool = False
+
+ if _compat.PYDANTIC_V1:
+ # to handle forward references in Pydantic v1
+ User.update_forward_refs(**locals()) # type: ignore[reportDeprecated]
+
+ address = Address(street="123 Main St")
+ user = User(name="Diana", address=address)
+ data = {"user": user}
+ json_bytes = openapi_dumps(data)
+ assert json_bytes == b'{"user":{"name":"Diana","address":{"street":"123 Main St"}}}'
+
+ address_with_city = Address(street="456 Oak Ave", city="Boston")
+ user_verified = User(name="Eve", address=address_with_city, verified=True)
+ data = {"user": user_verified}
+ json_bytes = openapi_dumps(data)
+ assert (
+ json_bytes == b'{"user":{"name":"Eve","address":{"street":"456 Oak Ave","city":"Boston"},"verified":true}}'
+ )
+
+ def test_pydantic_model_with_optional_fields(self) -> None:
+ class User(pydantic.BaseModel):
+ name: str
+ email: Union[str, None]
+ phone: Union[str, None]
+
+ model_with_none = User(name="Eve", email=None, phone=None)
+ data = {"model": model_with_none}
+ json_bytes = openapi_dumps(data)
+ assert json_bytes == b'{"model":{"name":"Eve","email":null,"phone":null}}'
+
+ model_with_values = User(name="Frank", email="frank@example.com", phone=None)
+ data = {"model": model_with_values}
+ json_bytes = openapi_dumps(data)
+ assert json_bytes == b'{"model":{"name":"Frank","email":"frank@example.com","phone":null}}'