From 3443da4b964d95d52447bb63a5e90f2a4315aa3c Mon Sep 17 00:00:00 2001 From: vvillait88 Date: Tue, 8 Sep 2026 19:12:56 -0400 Subject: [PATCH] Add the session kind option to create_session The API accepts kind on POST /v1/sessions: kyc runs identity verification, sign_in is registration-only and mints a sign_in-scoped credential. Merchants that key durable state on the account with no compliance policy need the second kind and could not request it through the SDK. Omitted when None, so existing callers keep the API default. Parity with the node SDK. Co-Authored-By: Claude Fable 5.1 --- README.md | 1 + agentscore/client.py | 20 +++++++++++++++++--- agentscore/types.py | 2 ++ pyproject.toml | 2 +- tests/test_client.py | 3 +++ uv.lock | 2 +- 6 files changed, 25 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2a53fda..426996e 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,7 @@ if status["status"] == "verified": # for an existing operator credential. client.create_session(address="0x...") client.create_session(operator_token="opc_...") # KYC refresh +client.create_session(kind="sign_in") # registration-only: account sign-in, no identity documents ``` ### Wallet resolution diff --git a/agentscore/client.py b/agentscore/client.py index 4223ff5..d8a6c76 100644 --- a/agentscore/client.py +++ b/agentscore/client.py @@ -4,7 +4,7 @@ import logging import time from importlib.metadata import version as _pkg_version -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, Literal import httpx @@ -308,12 +308,17 @@ def create_session( product_name: str | None = None, address: str | None = None, operator_token: str | None = None, + kind: Literal["kyc", "sign_in"] | None = None, ) -> SessionCreateResponse: """Create an assessment session for deferred scoring. ``address`` pre-associates the session with a known wallet (EVM ``0x...`` or Solana base58). ``operator_token`` pre-associates with an existing ``opc_...`` — - e.g. refresh KYC for a credential. + e.g. refresh KYC for a credential. ``kind`` selects the session kind: ``"kyc"`` + (the API default) runs identity verification; ``"sign_in"`` is registration-only + (the buyer signs in with an AgentScore account, no identity documents) and mints a + ``sign_in``-scoped credential, for merchants that key durable state on the account + without any compliance policy. """ body: dict[str, Any] = {} if context is not None: @@ -324,6 +329,8 @@ def create_session( body["address"] = address if operator_token is not None: body["operator_token"] = operator_token + if kind is not None: + body["kind"] = kind client = self._get_sync_client() return self._send_sync(lambda: client.post("/v1/sessions", json=body)) @@ -451,12 +458,17 @@ async def acreate_session( product_name: str | None = None, address: str | None = None, operator_token: str | None = None, + kind: Literal["kyc", "sign_in"] | None = None, ) -> SessionCreateResponse: """Create an assessment session for deferred scoring. ``address`` pre-associates the session with a known wallet (EVM ``0x...`` or Solana base58). ``operator_token`` pre-associates with an existing ``opc_...`` — - e.g. refresh KYC for a credential. + e.g. refresh KYC for a credential. ``kind`` selects the session kind: ``"kyc"`` + (the API default) runs identity verification; ``"sign_in"`` is registration-only + (the buyer signs in with an AgentScore account, no identity documents) and mints a + ``sign_in``-scoped credential, for merchants that key durable state on the account + without any compliance policy. """ body: dict[str, Any] = {} if context is not None: @@ -467,6 +479,8 @@ async def acreate_session( body["address"] = address if operator_token is not None: body["operator_token"] = operator_token + if kind is not None: + body["kind"] = kind client = self._get_async_client() return await self._send_async(lambda: client.post("/v1/sessions", json=body)) diff --git a/agentscore/types.py b/agentscore/types.py index 00be467..97cc402 100644 --- a/agentscore/types.py +++ b/agentscore/types.py @@ -395,6 +395,8 @@ class _SessionCreateResponseRequired(TypedDict): class SessionCreateResponse(_SessionCreateResponseRequired, total=False): + # The kind the session was minted with; absent on older API responses. + kind: Literal["kyc", "sign_in"] # Structured next_steps with action=deliver_verify_url_and_poll. next_steps: SessionCreateNextSteps # Cross-merchant memory hint on first session creation. diff --git a/pyproject.toml b/pyproject.toml index eb24ba8..d2f1958 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "agentscore-py" -version = "2.6.8" +version = "2.6.9" description = "Python client for the AgentScore APIs" readme = "README.md" license = "MIT" diff --git a/tests/test_client.py b/tests/test_client.py index cbc85f9..2c096a3 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -1023,10 +1023,12 @@ def test_create_session_with_first_class_fields(): client.create_session( context="wine purchase verification", product_name="Cabernet Reserve 2022", + kind="sign_in", ) body = json.loads(route.calls.last.request.content) assert body["context"] == "wine purchase verification" assert body["product_name"] == "Cabernet Reserve 2022" + assert body["kind"] == "sign_in" @respx.mock @@ -1037,6 +1039,7 @@ def test_create_session_omits_none_fields(): body = json.loads(route.calls.last.request.content) assert "context" not in body assert "product_name" not in body + assert "kind" not in body @respx.mock diff --git a/uv.lock b/uv.lock index 123ba44..7923dbe 100644 --- a/uv.lock +++ b/uv.lock @@ -7,7 +7,7 @@ prerelease-mode = "disallow" [[package]] name = "agentscore-py" -version = "2.6.8" +version = "2.6.9" source = { editable = "." } dependencies = [ { name = "httpx" },