From 4233f6af1bd0473514e9fa3abc638b86a112a647 Mon Sep 17 00:00:00 2001 From: Cowork Date: Tue, 6 Oct 2026 17:44:21 +0000 Subject: [PATCH 1/2] infinihash-kyc 0.2.0: sandbox keys, is_unscreened covers screeningErrors - create_sandbox_key(email): self-serve kyc_sbx_ key (KYC-API #163), plus a README section with the magic-name table. - is_unscreened() is also True when summary.screeningErrors is set: KYC-API now routes a case whose sanctions list was NOT SEARCHED to review with screeningErrors=["sanctions"] and no summary.error, and the SDK must not call that screened. Co-Authored-By: Claude Opus 5.5 --- README.md | 31 ++++++++++++++++-- infinihash_kyc/__init__.py | 4 +-- .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 478 bytes .../__pycache__/client.cpython-310.pyc | Bin 0 -> 9745 bytes infinihash_kyc/client.py | 31 ++++++++++++++++-- pyproject.toml | 2 +- .../test_client.cpython-311-pytest-9.1.1.pyc | Bin 25201 -> 0 bytes tests/test_client.py | 21 +++++++++++- 8 files changed, 80 insertions(+), 9 deletions(-) create mode 100644 infinihash_kyc/__pycache__/__init__.cpython-310.pyc create mode 100644 infinihash_kyc/__pycache__/client.cpython-310.pyc delete mode 100644 tests/__pycache__/test_client.cpython-311-pytest-9.1.1.pyc diff --git a/README.md b/README.md index b78de56..6d13821 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,37 @@ Python SDK for the [Infinihash KYC API](https://kyc.infinihash.com): KYC and KYB sanctions screening, and case reports. ```bash -pip install "git+https://github.com/Infinihash/kyc-python@v0.1.0" +pip install "git+https://github.com/Infinihash/kyc-python@v0.2.0" export INFINIHASH_KYC_KEY=... # your API key ``` +## Try it free: the sandbox + +No account and no billing. A sandbox key runs the real case lifecycle with +deterministic results, and no provider is ever called: + +```python +from infinihash_kyc import KYC, create_sandbox_key + +kyc = KYC(api_key=create_sandbox_key("you@example.com")) # kyc_sbx_..., shown once +case = kyc.submissions.create(entity_type="individual", entity_name="Jane SANCTIONED Doe") +kyc.submissions.run(case["id"]) +print(kyc.submissions.wait(case["id"])["summary"]["action"]) # block_report +``` + +| Entity name contains | Result | +|---|---| +| `SANCTIONED` | sanctions HIT, so the case is `failed` / `block_report` | +| `UNSCREENED` | the sanctions list is not searched, so the case ends in `review` and `is_unscreened()` is True | +| `PEP` | `pep_screen` goes to review, so the case gets `enhanced_review` | +| `ADVERSE` | `negative_news` goes to review (adverse media) | +| `LIVENESS_FAIL` | `liveness_check` fails | +| anything else | every check passes, and the case is `complete` / `approve` | + +Sandbox keys can create, run, read, list and export cases and screen names. +Routes that call paid providers (OCR, liveness, biometrics) return +`403 sandbox_route_unavailable`. Use a production key for those. + ## Screen a name ```python @@ -37,7 +64,7 @@ kyc.submissions.run(case["id"]) result = kyc.submissions.wait(case["id"]) # complete | review | failed if is_unscreened(result): - print("manual review:", result["summary"]["error"]) + print("manual review:", result["summary"].get("error") or result["summary"]["screeningErrors"]) else: print(result["summary"]["action"]) # approve | enhanced_review | manual_review | block_report pdf = kyc.submissions.export_pdf(case["id"]) diff --git a/infinihash_kyc/__init__.py b/infinihash_kyc/__init__.py index 7433b74..018e780 100644 --- a/infinihash_kyc/__init__.py +++ b/infinihash_kyc/__init__.py @@ -1,5 +1,5 @@ """Infinihash KYC Python SDK. See client.py for the full guide.""" -from .client import KYC, KYCError, ScreeningUnavailable, is_unscreened, ENTITY_TYPES, RISK_TIERS +from .client import KYC, KYCError, ScreeningUnavailable, is_unscreened, create_sandbox_key, ENTITY_TYPES, RISK_TIERS __all__ = ["KYC", "KYCError", "ScreeningUnavailable", "is_unscreened", "ENTITY_TYPES", "RISK_TIERS"] -__version__ = "0.1.0" +__version__ = "0.2.0" diff --git a/infinihash_kyc/__pycache__/__init__.cpython-310.pyc b/infinihash_kyc/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8b511135b93b014b4e72892afada7c2197c6532f GIT binary patch literal 478 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 literal 0 HcmV?d00001 diff --git a/infinihash_kyc/client.py b/infinihash_kyc/client.py index a3e3366..db12c87 100644 --- a/infinihash_kyc/client.py +++ b/infinihash_kyc/client.py @@ -127,8 +127,33 @@ def export_pdf(self, submission_id: str) -> bytes: def is_unscreened(submission: Dict[str, Any]) -> bool: - """True when the case finished without being screened (fail-closed review).""" - return bool(((submission or {}).get("summary") or {}).get("error")) + """True when the case finished without being fully screened (fail-closed review). + + Either no checklist ran (``summary.error``), or a screen could not be + performed (``summary.screeningErrors``, e.g. a sanctions list that was not + searched). Such a case is never ``complete``; treat it as unscreened, not clear. + """ + summary = (submission or {}).get("summary") or {} + return bool(summary.get("error") or summary.get("screeningErrors")) + + +def create_sandbox_key(email: str, base_url: Optional[str] = None, timeout: float = DEFAULT_TIMEOUT) -> str: + """Self-serve sandbox key (``kyc_sbx_...``), returned once. No account needed. + + Sandbox keys run the real case lifecycle with deterministic results and are + never billed. Magic entity names: SANCTIONED, UNSCREENED, PEP, ADVERSE, + LIVENESS_FAIL; any other name passes. See ``GET /api/sandbox``. + """ + url = (base_url or os.environ.get("INFINIHASH_KYC_URL") or DEFAULT_BASE_URL).rstrip("/") + r = requests.post(url + "/api/sandbox/keys", json={"email": email}, timeout=timeout, + headers={"User-Agent": "infinihash-kyc-python/0.2.0"}) + if r.status_code != 201: + try: + msg = r.json().get("error") + except ValueError: + msg = r.text[:200] + raise KYCError(r.status_code, msg or r.reason) + return r.json()["apiKey"] class KYC: @@ -140,7 +165,7 @@ def __init__(self, api_key: Optional[str] = None, base_url: Optional[str] = None self.base_url = (base_url or os.environ.get("INFINIHASH_KYC_URL") or DEFAULT_BASE_URL).rstrip("/") self.timeout = timeout self._s = session or requests.Session() - self._s.headers.update({"X-API-Key": self.api_key, "User-Agent": "infinihash-kyc-python/0.1.0"}) + self._s.headers.update({"X-API-Key": self.api_key, "User-Agent": "infinihash-kyc-python/0.2.0"}) self.submissions = _Submissions(self) def screen(self, name: str, country: Optional[str] = None, dob: Optional[str] = None) -> Dict[str, Any]: diff --git a/pyproject.toml b/pyproject.toml index 8456ce5..24536c0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "infinihash-kyc" -version = "0.1.0" +version = "0.2.0" description = "Python SDK for the Infinihash KYC API (KYC/KYB cases, sanctions screening)." readme = "README.md" requires-python = ">=3.8" diff --git a/tests/__pycache__/test_client.cpython-311-pytest-9.1.1.pyc b/tests/__pycache__/test_client.cpython-311-pytest-9.1.1.pyc deleted file mode 100644 index 0a14978809510d7ed17829e5358a2c26aeb05d0b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25201 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 diff --git a/tests/test_client.py b/tests/test_client.py index 95528ef..e0481fa 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -7,7 +7,7 @@ import pytest -from infinihash_kyc import KYC, KYCError, ScreeningUnavailable, is_unscreened +from infinihash_kyc import KYC, KYCError, ScreeningUnavailable, create_sandbox_key, is_unscreened STATE = {} @@ -55,6 +55,10 @@ def do_POST(self): return self._send(400, {"error": "entityName must be 2–300 characters"}) return self._send(201, {"id": "sub_1", "status": "pending", "entityType": b["entityType"], "requiredChecks": ["sanctions_ofac", "pep"], "_links": {}}) + if self.path == "/api/sandbox/keys": + if "@" not in b.get("email", ""): + return self._send(400, {"error": "a valid email is required"}) + return self._send(201, {"apiKey": "kyc_sbx_" + "0" * 48, "tier": "sandbox"}) if self.path == "/api/kyc/sub_1/run": return self._send(200, {"message": "Checks started", "submissionId": "sub_1"}) if self.path == "/api/kyc/screen": @@ -146,3 +150,18 @@ def test_api_key_is_required(monkeypatch): monkeypatch.delenv("INFINIHASH_KYC_KEY", raising=False) with pytest.raises(ValueError): KYC() + + +def test_a_sanctions_screening_error_counts_as_unscreened(kyc): + # KYC-API 2026-10-06: a sanctions list that was NOT SEARCHED routes the case + # to review with screeningErrors=['sanctions'] but no summary.error. + STATE["final"] = {"id": "sub_1", "status": "review", + "summary": {"action": "manual_review", "screeningErrors": ["sanctions"]}, "checks": []} + assert is_unscreened(kyc.submissions.wait("sub_1", poll=0.01)) + + +def test_create_sandbox_key(kyc): + assert create_sandbox_key("dev@example.com", base_url=kyc.base_url).startswith("kyc_sbx_") + with pytest.raises(KYCError) as e: + create_sandbox_key("nope", base_url=kyc.base_url) + assert e.value.status == 400 From 4a4f708f237434b5092554327072549b91f4ad42 Mon Sep 17 00:00:00 2001 From: Cowork Date: Tue, 6 Oct 2026 17:44:28 +0000 Subject: [PATCH 2/2] chore: stop tracking __pycache__ Co-Authored-By: Claude Opus 5.5 --- .gitignore | 3 +++ .../__pycache__/__init__.cpython-310.pyc | Bin 478 -> 0 bytes .../__pycache__/__init__.cpython-311.pyc | Bin 521 -> 0 bytes .../__pycache__/client.cpython-310.pyc | Bin 9745 -> 0 bytes .../__pycache__/client.cpython-311.pyc | Bin 12606 -> 0 bytes 5 files changed, 3 insertions(+) create mode 100644 .gitignore delete mode 100644 infinihash_kyc/__pycache__/__init__.cpython-310.pyc delete mode 100644 infinihash_kyc/__pycache__/__init__.cpython-311.pyc delete mode 100644 infinihash_kyc/__pycache__/client.cpython-310.pyc delete mode 100644 infinihash_kyc/__pycache__/client.cpython-311.pyc diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..75c6182 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +__pycache__/ +*.pyc +.pytest_cache/ diff --git a/infinihash_kyc/__pycache__/__init__.cpython-310.pyc b/infinihash_kyc/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 8b511135b93b014b4e72892afada7c2197c6532f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 478 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 diff --git a/infinihash_kyc/__pycache__/client.cpython-311.pyc b/infinihash_kyc/__pycache__/client.cpython-311.pyc deleted file mode 100644 index 4769726e1a2c56f3c58e98a91961c7f5c4e90ef6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12606 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